Для дальнейшей работы вам понадобится проект, который по мере прохождения спринта вы поместите в контейнеры Docker локально, на вашем компьютере. Этим проектом будет api_yamdb. Склонируйте его к себе на компьютер из репозитория на GitHub.
Затем удалите лишние файлы. Они были нужны для того, чтобы сдать проект в другом спринте, сейчас они вам не понадобятся.
Читайте комментарии к структуре и наводите порядок:
Скопировать код
SCHEME
Корневая папка проекта
├── api_yamdb/
│ ├── api/ <— Директория приложения api
│ │ └── # Файлы приложения
│ ├── api_yamdb/
│ │ ├── __init__.py
│ │ ├── settings.py
│ │ ├── urls.py
│ │ └── wsgi.py
│ ├── reviews/ <— Директория приложения reviews
│ │ └── # Файлы приложения
│ ├── static <— Директория для сборки статических файлов проекта
│ │ └── redoc.yaml
│ ├── templates
│ │ └── redoc.html
│ └── manage.py
├── tests/ <— Эту директорию нужно удалить
├── .gitignore
├── pytest.ini <— Этот файл нужно удалить
├── README.md
├── requirements.txt
└── setup.cfg <— Этот тоже удаляйте
Теперь можно приступать к упаковке проекта в контейнеры.
Dockerfile
В корневой директории проекта api_yamdb создайте файл под названием Dockerfile. Важно, чтобы название начиналось именно с большой буквы. Расширение указывать не нужно.
У вас должен получиться проект вот с такой структурой:
Скопировать код
SCHEME
Корневая папка проекта
├── api_yamdb/
│ ├── api/ <— Директория приложения api
│ │ └── # Файлы приложения
│ ├── api_yamdb/
│ │ ├── __init__.py
│ │ ├── settings.py
│ │ ├── urls.py
│ │ └── wsgi.py
│ ├── reviews/ <— Директория приложения reviews
│ │ └── # Файлы приложения
│ ├── static <— Директория для сборки статических файлов проекта
│ │ └── redoc.yaml
│ ├── templates
│ │ └── redoc.html
│ └── manage.py
├── .gitignore
├── Dockerfile <— Новый Dockerfile
├── README.md
└── requirements.txt
Докерфайл — это текстовый документ, в котором содержатся инструкции для сборки образов. Они короткие и понятные. Инструкции служат для системы Docker перечнем действий по которым Docker будет собирать образ для контейнеров. Каждая из них пишется заглавными буквами, а их выполнение, как и в Python, происходит последовательно одна за другой.
Откройте Dockerfile, добавьте в него инструкции для сборки образов и сохраните изменения:
Скопировать код
DOCKER
# Создать образ на основе базового слоя python (там будет ОС и интерпретатор Python).
# 3.7 — используемая версия Python.
# slim — обозначение того, что образ имеет только необходимые компоненты для запуска,
# он не будет занимать много места при развёртывании.
FROM python:3.7-slim
# Запустить команду создания директории внутри контейнера
RUN mkdir /app
# Скопировать с локального компьютера файл зависимостей
# в директорию /app.
COPY requirements.txt /app
# Выполнить установку зависимостей внутри контейнера.
RUN pip3 install -r /app/requirements.txt —no-cache-dir
# Скопировать содержимое директории /api_yamdb c локального компьютера
# в директорию /app.
COPY api_yamdb/ /app
# Сделать директорию /app рабочей директорией.
WORKDIR /app
# Выполнить запуск сервера разработки при старте контейнера.
CMD [«python3», «manage.py», «runserver», «0:8000»]
FROM
Dockerfile всегда начинается с этой инструкции. Она определяет базовый образ, на основе которого создаётся ваш локальный образ:
Скопировать код
DOCKER
FROM <image>:<tag>
# <image> — имя базового образа, например python
# <tag> — необязательный параметр, указывает конкретную версию образа — 3.7-slim
Если не указать версию образа, то будет установлена последняя версия (с тегом latest — англ. «самый последний»), но хорошей практикой является явное указание версии.
В Dockerfile для вашего проекта инструкция записана так:
Скопировать код
DOCKER
FROM python:3.7-slim
В основе большинства образов лежит дистрибутив операционной системы. У вас это дистрибутив Linux (если конкретнее — Debian) с предустановленным интерпретатором python 3.7.
Версия slim подразумевает, что в дистрибутив установлены только самые необходимые пакеты. Это значит, что готовый образ займёт минимум места на вашем компьютере и будет быстро собираться..
Есть и другие версии базовых образов Python — полная (тогда после номера версии в инструкции ничего писать не нужно) и alpine. Они отличаются скоростью сборки и весом.
Самая медленная и тяжёлая — полная версия образа Python. Она содержит все пакеты и службы, которые зачастую не нужны для развёртывания приложения.
Alpine — быстрая и лёгкая версия, но со своими недостатками. Для неё нужно вручную прописывать в докерфайле нужные пакеты ОС для сборки приложения.
RUN
Выполняет команды внутри контейнера так, как если бы эти команды выполнялись в терминале. Например, можно вызвать утилиту или просто создать директорию через mkdir. Чтобы эта инструкция работала — base image должен содержать все необходимые утилиты:
Скопировать код
DOCKER
RUN <команда>
В вашем Dockerfile эта инструкция запускает внутри контейнера команду для установки зависимостей:
Скопировать код
DOCKER
RUN pip3 install -r /app/requirements.txt —no-cache-dir
COPY
Копирует файлы и директории из указанной локальной директории в директорию контейнера:
Скопировать код
DOCKER
COPY <файл/директория> <путь-внутри-контейнера>
# альтернативный вариант записи:
COPY [«<файл/директория>», «<путь-внутри-контейнера>»]
В вашем докерфайле файл зависимостей копируется в директорию /app контейнера:
Скопировать код
DOCKER
COPY requirements.txt /app
Второй раз инструкция COPY используется, чтобы скопировать содержимое локальной директории в директорию контейнера:
Скопировать код
DOCKER
COPY api_yamdb/ /app
Если целевой директории для копирования нет, то инструкция COPY создаёт её.
Когда нужно, чтобы какие-то файлы или директории не попали в контейнер, можно перечислить их в .dockerignore, принцип такой же, как с .gitignore.
WORKDIR
Задаёт в образе директорию, из которой будут выполняться все команды, следующие за этой инструкцией. Если такой директории не существует инструкция WORKDIR создаст её.
WORKDIR работает аналогично команде cd в терминале:
Скопировать код
DOCKER
WORKDIR <путь-внутри-контейнера>
После выполнения инструкции WORKDIR /app все команды RUN, CMD, COPY в образе будут выполняться внутри этой директории.
Инструкций WORKDIR внутри Dockerfile может быть несколько. В инструкции лучше указывать абсолютный путь до директории. Если указывать относительный, то путь к новой директории будет начинаться с текущей:
Скопировать код
DOCKER
WORKDIR /workdir
WORKDIR project
WORKDIR app
# Вернёт текущую директорию — /workdir/project/app
RUN pwd
CMD
Запускает что-нибудь, например bash-скрипт или сервер для приложения, при старте контейнера. У этой инструкции есть три особенности:
в Dockerfile должна быть только одна такая инструкция. Если их будет несколько, выполнится только та, что записана самой последней, остальные проигнорируются;
все элементы списка заключаются в двойные кавычки — синтаксис соответствует формату JSON;
первым элементом указывается исполняемый файл, например, интерпретатор python; последующими аргументами указываются ключи и параметры для запуска исполняемого файла.
Скопировать код
DOCKER
CMD [«исполняемый_файл», «аргумент_1», …, «аргумент_2»]
Такая инструкция в вашем Dockerfile запускает dev-сервер приложения внутри контейнера:
Скопировать код
DOCKER
CMD [«python3», «manage.py», «runserver», «0:8000»]
LABEL
Эту инструкцию вы не использовали в своём докерфайле, но знать о ней полезно. Она задаёт служебную информацию об образе. Это может быть информация об авторе (author=…), о версии образа (version=…), о дате релиза или о числе сломанных в процессе работы клавиатур (broken_keyboards=…). Пары «ключ-значение» записываются через пробел, ключи придумывает сам разработчик:
Скопировать код
BASH
LABEL <ключ>=<значение> <ключ>=<значение> …
Например:
Скопировать код
LABEL author=’praktikum@yandex.ru’ version=1 broken_keyboards=5
ENV
Этой инструкции в вашем докерфайле пока тоже нет, но она вам пригодится в будущем. Инструкция ENV задаёт переменные окружения в контейнере:
Скопировать код
BASH
ENV <ключ> <значение>
Например:
Скопировать код
ENV DATABASE_NAME yamdb
ENV DATABASE_PORT 5432
Использование ENV — хорошая практика. Секретные данные (ключи, пароли, токены) лучше не хранить в коде.
Все инструкции Dockerfile описаны, настала пора собрать образ с вашим приложением! А если вы хотите узнать больше об инструкциях, заглядывайте в документацию.
Время собирать образ
Запустите терминал. Убедитесь, что вы находитесь в той же директории, где сохранён Dockerfile, и запустите сборку образа:
Скопировать код
BASH
docker build -t yamdb .
build — команда сборки образа по инструкциям из Dockerfile.
-t yamdb — ключ, который позволяет задать имя образу, а потом и само имя.
. — точка в конце команды — путь до Dockerfile, на основе которого производится сборка..
Если в процессе сборки не возникло ошибок, терминал покажет подобный вывод:
image
Образ собран. Файл с ним появится в директории, указанной в настройках Docker — на локальном диске или виртуальном, если вы работаете через Hyper-V.
Теперь можно приступать к работе с образом. В терминале это делается через консольную утилиту docker. Пользователи Windows и Mac могут также работать через приложение Docker Desktop. Начнём с консоли.
Посмотрите, какие образы есть на вашем компьютере. В терминале это делается через менеджер образов docker image:
Скопировать код
docker image ls
Результат работы команды будет примерно таким:
image
Если в вашем списке больше образов, удалите их, они только зря занимают место на диске. С этим вам поможет команда:
Скопировать код
BASH
docker image rm IMAGE_ID
Запуск контейнера
Теперь можно запустить контейнер. В терминале это делается командой:
Скопировать код
BASH
docker run —name <имя контейнера> -it -p 8000:8000 yamdb
run — команда запуска нового контейнера.
—name my_project — ключ, который позволяет задать имя контейнеру, и само имя.
-it — комбинация этих ключей даёт возможность передавать в контейнер команды из вашего терминала.
-p 8000:8000 — указывает публичный порт контейнера. Левая часть — внешний порт контейнера, правая — порт, на который будет перенаправлен запрос.
yamdb — образ, из которого будет запущен контейнер.
Введите в адресную строку браузера localhost:8000: приложение запущено и работает!
Управление контейнерами
Сейчас у вас есть только один контейнер. Вы его только что запустили и знаете точно, что он работает. Но когда контейнеров станет больше, всё будет не так очевидно. Одни контейнеры вы будете запускать, другие останавливать, и так по кругу. В процессе работы будет полезно знать, какие контейнеры запущены, а какие остановлены.
Откройте новое окно терминала и выполните команду:
Скопировать код
BASH
docker container ls
В терминал будет выведена таблица со списком запущенных контейнеров:
в колонку CONTAINER ID выводится уникальное имя контейнера;
в колонку IMAGE — имя образа, на основе которого создан контейнер;
COMMAND — это команда, которая выполнилась в контейнере после сборки;
CREATED — когда был создан контейнер;
STATUS — состояние контейнера;
PORTS — порты контейнера (в вашем случае приложение в контейнере запущено на 8000 порте, и этот же порт «проброшен» наружу: по этому порту можно обратиться к приложению в контейнере);
NAMES — человекочитаемое имя контейнера, его можно задать, чтобы обращаться к контейнеру не по CONTAINER ID, а по имени (контейнер можно переименовать командой docker container renameCONTAINER NEW_NAME).
Контейнеры можно останавливать и запускать, при этом заново собирать контейнер не придётся.
Остановите запущенный контейнер через терминал:
Скопировать код
docker container stop <CONTAINER ID>
Проверьте список запущенных контейнеров:
Скопировать код
docker container ls
Контейнер остановлен — список запущенных контейнеров пуст, приложение в браузере больше недоступно.
Посмотрите список всех контейнеров, выполнив команду с ключом -a (all, «все»):
Скопировать код
BASH
docker container ls -a
Вы увидите, что контейнер никуда не делся, просто он неактивен.
Остановленный контейнер можно запустить, собирать его из образа уже не нужно:
Скопировать код
BASH
docker container start <CONTAINER ID>
Откройте приложение в браузере: всё работает, контейнер запущен.
Список всех команд для работы с контейнером можно вызвать через:
Скопировать код
BASH
docker container
image
Работа через Docker Desktop
Пользователи Windows и Mac могут управлять собранными образами и контейнерами и через приложение Docker Desktop. Это удобно и быстро, но старайтесь отдавать предпочтение терминалу, так как при работе на удалённом сервере в вашем распоряжении будет только строка терминала.
Коротко об основных функциях приложения
Просмотр списка образов: в меню слева кликните на пункт Images.
image
Запуск контейнера: наведите курсор на нужный образ и нажмите кнопку RUN.
image
Просмотр списка контейнеров: в меню слева кликните на пункт Containers / Apps. Запущенные контейнеры будут обозначены зелёной иконкой.
image
Управление контейнером: наведите курсор на нужный контейнер — справа появятся кнопки.
А что внутри контейнера?
В контейнере запущена операционная система, и в ней можно работать через терминал точно так же, как в ОС вашего компьютера или удалённого сервера. Для этого надо войти в запущенный контейнер.
Для входа в контейнер выполните команду:
Скопировать код
docker exec -it <CONTAINER ID> bash
exec — запустит команду внутри контейнера.
-it — комбинация ключей, которая передаёт команды из вашего терминала в контейнер.
bash — запустит терминал внутри контейнера.
Для эксперимента создайте в контейнере какой-нибудь файл с помощью команды touch (работа в контейнере не отличается от работы в обычной ОС). После этого покиньте контейнер: выполните команду exit.
Остановите контейнер, затем запустите его снова и проверьте, на месте ли ваш файл.
Файл на месте?
Правильный ответ
Да
Правильно, контейнер, хоть и был остановлен, всё ещё существует. Все данные в нём сохранились.
Нет
Время убивать контейнер. Сейчас приложение внутри контейнера запущено на dev-сервере, а в следующих уроках вашей задачей будет развернуть всю инфраструктуру для деплоя на боевой сервер. Инструкции для сборки изменятся, а значит, текущий контейнер вам уже не понадобится.
Выполните команду:
Скопировать код
docker container rm <CONTAINER ID>
Контейнер безвозвратно удалён вместе со всеми данными. Идём дальше!
Время на прочтение
3 мин
Количество просмотров 4.7K
В данном туториале мы рассмотрим, как быстро развернуть LEMP-стэк на виртуальный сервер VPS, используя технологию контейнеризации на базе Docker для сайта под управлением CMS DataLife Engine (DLE).
Предполагается, что у вас уже установлен движок контейнеризации Docker, а также Compose для одновременно развертывания нескольких контейнеров и управления ими.
Для начала создадим структуру каталогов для сайта:
mkdir -p /data/project/{app,db,log,src}
где, app это директория для хранения файлов сайта, в db будут хранятся файлы баз данных MySQL, в папке log хранятся логи веб-сервера NGINX, а в src исходники и конфигурационные файлы для сборки кастомных контейнеров. Файл docker-compose.yml содержит инструкции для развертывания контейнеров Docker.
В этом файле указываем, например:
-
Откуда взять Dockerfile для создания кастомного образа
-
Какие порты привязать к хост-машине
-
Где хранить данные
-
и т.д.
Compose считывает этот файл и выполняет команды. Создадим файл /data/project/docker-compose.yml со следующим содержимым:
version: '3.7'
services:
# NGINX Service
web:
image: nginx:latest
container_name: web
ports:
- "80:80"
volumes:
- ./src/nginx_default_vhost.conf:/etc/nginx/conf.d/default.conf
- ./app:/var/www/html
- ./log:/var/log/nginx
# PHP Service
app:
build:
context: ./src
dockerfile: Dockerfile-PHP-FPM
container_name: app
working_dir: /var/www/html
volumes:
- ./app:/var/www/html
# MySQL Service
db:
image: mariadb:10.5.10
container_name: db
environment:
MARIADB_ROOT_PASSWORD: 1234567890
volumes:
- ./db:/var/lib/mysql
Создадим конфиг виртуального хоста в файле /data/project/src/nginx_default_vhost.conf с проксированием контента на бэкэнд:
server {
listen 0.0.0.0:80 default_server;
server_name localhost;
root /var/www/html;
index index.php index.html;
location / {
try_files $uri $uri/ =404;
}
location ~ .php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+.php)(/.+)$;
fastcgi_pass app:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
}
Для PHP мы будем создавать кастомный образ на базе php:7.4-fpm с добавлением расширений gd, mysqli и zip, которые необходимы для работы с движком DLE. Создадим файл /data/project/src/Dockerfile-PHP-FPM со следующим содержимым:
FROM php:7.4-fpm
RUN apt-get update && apt-get install -y
libfreetype6-dev
libjpeg62-turbo-dev
libpng-dev
libzip-dev
zip
&& docker-php-ext-configure gd --with-freetype --with-jpeg
&& docker-php-ext-install -j$(nproc) gd
&& docker-php-ext-install mysqli
&& docker-php-ext-install zip
Скачиваем пробную версию движка DLE с официального сайта. При использовании демоверсии, существуют ограничения. Подробно вы можете ознакомиться на сайте: http://dle-news.ru
wget https://dle-news.ru/files/dle_trial.zip
Разархивируем файлы в папку /data/project/app:
unzip dle_trial.zip "upload/*" -d /tmp && mv /tmp/upload/* /data/project/app/
Назначаем права доступа папкам согласно документации движка:
chmod 777 /data/project/app/{templates,engine/{data,cache}}
chmod -R 777 /data/project/app/{backup,uploads}
Запускаем контейнеры:
docker-compose -f /data/project/docker-compose.yml up -d -–build
Открываем в браузере адрес сервера и приступаем к установке движка. Для продолжения установки необходимо создать базу данных и пользователя к ней. Подключаемся к контейнеру db:
docker exec -it db bash
Затем подключаемся к службе mysql:
mysql -u root -p"1234567890"
Выполняем SQL запрос для создания базы testdb:
create database testdb character set utf8 collate utf8_bin;
Создаем пользователя testuser и предоставляем права доступа на базу testdb:
grant all privileges on testdb.* to testuser@'%' identified by 'passwd';
Теперь заполняем поля на сайте, указываем в качестве сервера MySQL, имя контейнера БД, в нашем случае это db.
Поздравляю! Вы успешно установили CMS Datalife Engine с использованием технологии контейнеризации приложений Docker.
Время на прочтение
9 мин
Количество просмотров 545K
Автор статьи, перевод которой мы сегодня публикуем, говорит, что она предназначена для тех разработчиков, которые хотят изучить Docker Compose и идут к тому, чтобы создать своё первое клиент-серверное приложение с использованием Docker. Предполагается, что читатель этого материала знаком с основами Docker. Если это не так — можете взглянуть на эту серию материалов, на эту публикацию, где основы Docker рассмотрены вместе с основами Kubernetes, и на эту статью для начинающих.
Что такое Docker Compose?
Docker Compose — это инструментальное средство, входящее в состав Docker. Оно предназначено для решения задач, связанных с развёртыванием проектов.
Изучая основы Docker, вы могли столкнуться с созданием простейших приложений, работающих автономно, не зависящих, например, от внешних источников данных или от неких сервисов. На практике же подобные приложения — редкость. Реальные проекты обычно включают в себя целый набор совместно работающих приложений.
Как узнать, нужно ли вам, при развёртывании некоего проекта, воспользоваться Docker Compose? На самом деле — очень просто. Если для обеспечения функционирования этого проекта используется несколько сервисов, то Docker Compose может вам пригодиться. Например, в ситуации, когда создают веб-сайт, которому, для выполнения аутентификации пользователей, нужно подключиться к базе данных. Подобный проект может состоять из двух сервисов — того, что обеспечивает работу сайта, и того, который отвечает за поддержку базы данных.
Технология Docker Compose, если описывать её упрощённо, позволяет, с помощью одной команды, запускать множество сервисов.
Разница между Docker и Docker Compose
Docker применяется для управления отдельными контейнерами (сервисами), из которых состоит приложение.
Docker Compose используется для одновременного управления несколькими контейнерами, входящими в состав приложения. Этот инструмент предлагает те же возможности, что и Docker, но позволяет работать с более сложными приложениями.
Docker (отдельный контейнер) и Docker Compose (несколько контейнеров)
Типичный сценарий использования Docker Compose
Docker Compose — это, в умелых руках, весьма мощный инструмент, позволяющий очень быстро развёртывать приложения, отличающиеся сложной архитектурой. Сейчас мы рассмотрим пример практического использования Docker Compose, разбор которого позволит вам оценить те преимущества, которые даст вам использование Docker Compose.
Представьте себе, что вы являетесь разработчиком некоего веб-проекта. В этот проект входит два веб-сайта. Первый позволяет людям, занимающимся бизнесом, создавать, всего в несколько щелчков мышью, интернет-магазины. Второй нацелен на поддержку клиентов. Эти два сайта взаимодействуют с одной и той же базой данных.
Ваш проект становится всё популярнее, и оказывается, что мощности сервера, на котором он работает, уже недостаточно. В результате вы решаете перевести весь проект на другую машину.
К сожалению, нечто вроде Docker Compose вы не использовали. Поэтому вам придётся переносить и перенастраивать сервисы по одному, надеясь на то, что вы, в процессе этой работы, ничего не забудете.
Если же вы используете Docker Compose, то перенос вашего проекта на новый сервер — это вопрос, который решается выполнением нескольких команд. Для того чтобы завершить перенос проекта на новое место, вам нужно лишь выполнить кое-какие настройки и загрузить на новый сервер резервную копию базы данных.
Разработка клиент-серверного приложения с использованием Docker Compose
Теперь, когда вы знаете о том, для чего мы собираемся использовать Docker Compose, пришло время создать ваше первое клиент-серверное приложение с использованием этого инструмента. А именно, речь идёт о разработке небольшого веб-сайта (сервера) на Python, который умеет выдавать файл с фрагментом текста. Этот файл у сервера запрашивает программа (клиент), тоже написанная на Python. После получения файла с сервера программа выводит текст, хранящийся в нём, на экран.
Обратите внимание на то, что мы рассчитываем на то, что вы владеете основами Docker, и на то, что у вас уже установлена платформа Docker.
Приступим к работе над проектом.
▍1. Создание проекта
Для того чтобы построить ваше первое клиент-серверное приложение, предлагаю начать с создания папки проекта. Она должна содержать следующие файлы и папки:
- Файл
docker-compose.yml
. Это файл Docker Compose, который будет содержать инструкции, необходимые для запуска и настройки сервисов. - Папка
server
. Она будет содержать файлы, необходимые для обеспечения работы сервера. - Папка
client
. Здесь будут находиться файлы клиентского приложения.
В результате содержимое главной папки вашего проекта должно выглядеть так:
.
├── client/
├── docker-compose.yml
└── server/
2 directories, 1 file
▍2. Создание сервера
Тут мы, в процессе создания сервера, затронем некоторые базовые вещи, касающиеся Docker.
2a. Создание файлов
Перейдите в папку server
и создайте в ней следующие файлы:
- Файл
server.py
. В нём будет находиться код сервера. - Файл
index.html
. В этом файле будет находиться фрагмент текста, который должно вывести клиентское приложение. - Файл
Dockerfile
. Это — файл Docker, который будет содержать инструкции, необходимые для создания окружения сервера.
Вот как должно выглядеть содержимое вашей папки server/
:
.
├── Dockerfile
├── index.html
└── server.py
0 directories, 3 files
2b. Редактирование Python-файла.
Добавим в файл server.py
следующий код:
#!/usr/bin/env python3
# Импорт системных библиотек python.
# Эти библиотеки будут использоваться для создания веб-сервера.
# Вам не нужно устанавливать что-то особенное, эти библиотеки устанавливаются вместе с Python.
import http.server
import socketserver
# Эта переменная нужна для обработки запросов клиента к серверу.
handler = http.server.SimpleHTTPRequestHandler
# Тут мы указываем, что сервер мы хотим запустить на порте 1234.
# Постарайтесь запомнить эти сведения, так как они нам очень пригодятся в дальнейшем, при работе с docker-compose.
with socketserver.TCPServer(("", 1234), handler) as httpd:
# Благодаря этой команде сервер будет выполняться постоянно, ожидая запросов от клиента.
httpd.serve_forever()
Этот код позволяет создать простой веб-сервер. Он будет отдавать клиентам файл index.html
, содержимое которого позже будет выводиться на веб-странице.
2c. Редактирование HTML-файла
В файл index.html
добавим следующий текст:
Docker-Compose is magic!
Этот текст будет передаваться клиенту.
2d. Редактирование файла Dockerfile
Сейчас мы создадим простой файл Dockerfile
, который будет отвечать за организацию среды выполнения для Python-сервера. В качестве основы создаваемого образа воспользуемся официальным образом, предназначенным для выполнения программ, написанных на Python. Вот содержимое Dockerfile:
# На всякий случай напоминаю, что Dockerfile всегда должен начинаться с импорта базового образа.
# Для этого используется ключевое слово 'FROM'.
# Здесь нам нужно импортировать образ python (с DockerHub).
# В результате мы, в качестве имени образа, указываем 'python', а в качестве версии - 'latest'.
FROM python:latest
# Для того чтобы запустить в контейнере код, написанный на Python, нам нужно импортировать файлы 'server.py' и 'index.html'.
# Для того чтобы это сделать, мы используем ключевое слово 'ADD'.
# Первый параметр, 'server.py', представляет собой имя файла, хранящегося на компьютере.
# Второй параметр, '/server/', это путь, по которому нужно разместить указанный файл в образе.
# Здесь мы помещаем файл в папку образа '/server/'.
ADD server.py /server/
ADD index.html /server/
# Здесь мы воспользуемся командой 'WORKDIR', возможно, новой для вас.
# Она позволяет изменить рабочую директорию образа.
# В качестве такой директории, в которой будут выполняться все команды, мы устанавливаем '/server/'.
WORKDIR /server/
Теперь займёмся работой над клиентом.
▍3. Создание клиента
Создавая клиентскую часть нашего проекта, мы попутно вспомним некоторые основы Docker.
3a. Создание файлов
Перейдите в папку вашего проекта client
и создайте в ней следующие файлы:
- Файл
client.py
. Тут будет находиться код клиента. - Файл
Dockerfile
. Этот файл играет ту же роль, что и аналогичный файл в папке сервера. А именно, он содержит инструкцию, описывающую создание среды для выполнения клиентского кода.
В результате ваша папка client/
на данном этапе работы должна выглядеть так:
.
├── client.py
└── Dockerfile
0 directories, 2 files
3b. Редактирование Python-файла
Добавим в файл client.py
следующий код:
#!/usr/bin/env python3
# Импортируем системную библиотеку Python.
# Она используется для загрузки файла 'index.html' с сервера.
# Ничего особенного устанавливать не нужно, эта библиотека устанавливается вместе с Python.
import urllib.request
# Эта переменная содержит запрос к 'http://localhost:1234/'.
# Возможно, сейчас вы задаётесь вопросом о том, что такое 'http://localhost:1234'.
# localhost указывает на то, что программа работает с локальным сервером.
# 1234 - это номер порта, который вам предлагалось запомнить при настройке серверного кода.
fp = urllib.request.urlopen("http://localhost:1234/")
# 'encodedContent' соответствует закодированному ответу сервера ('index.html').
# 'decodedContent' соответствует раскодированному ответу сервера (тут будет то, что мы хотим вывести на экран).
encodedContent = fp.read()
decodedContent = encodedContent.decode("utf8")
# Выводим содержимое файла, полученного с сервера ('index.html').
print(decodedContent)
# Закрываем соединение с сервером.
fp.close()
Благодаря этому коду клиентское приложение может загрузить данные с сервера и вывести их на экран.
3c. Редактирование файла Dockerfile
Как и в случае с сервером, мы создаём для клиента простой Dockerfile
, ответственный за формирование среды, в которой будет работать клиентское Python-приложение. Вот код клиентского Dockerfile
:
# То же самое, что и в серверном Dockerfile.
FROM python:latest
# Импортируем 'client.py' в папку '/client/'.
ADD client.py /client/
# Устанавливаем в качестве рабочей директории '/client/'.
WORKDIR /client/
▍4. Docker Compose
Как вы могли заметить, мы создали два разных проекта: сервер и клиент. У каждого из них имеется собственный файл Dockerfile
. До сих пор всё происходящее не выходит за рамки основ работы с Docker. Теперь же мы приступаем к работе с Docker Compose. Для этого обратимся к файлу docker-compose.yml
, расположенному в корневой папке проекта.
Обратите внимание на то, что тут мы не стремимся рассмотреть абсолютно все команды, которые можно использовать в docker-compose.yml
. Наша главная цель — разобрать практический пример, дающий вам базовые знания по Docker Compose.
Вот код, который нужно поместить в файл docker-compose.yml
:
# Файл docker-compose должен начинаться с тега версии.
# Мы используем "3" так как это - самая свежая версия на момент написания этого кода.
version: "3"
# Следует учитывать, что docker-composes работает с сервисами.
# 1 сервис = 1 контейнер.
# Сервисом может быть клиент, сервер, сервер баз данных...
# Раздел, в котором будут описаны сервисы, начинается с 'services'.
services:
# Как уже было сказано, мы собираемся создать клиентское и серверное приложения.
# Это означает, что нам нужно два сервиса.
# Первый сервис (контейнер): сервер.
# Назвать его можно так, как нужно разработчику.
# Понятное название сервиса помогает определить его роль.
# Здесь мы, для именования соответствующего сервиса, используем ключевое слово 'server'.
server:
# Ключевое слово "build" позволяет задать
# путь к файлу Dockerfile, который нужно использовать для создания образа,
# который позволит запустить сервис.
# Здесь 'server/' соответствует пути к папке сервера,
# которая содержит соответствующий Dockerfile.
build: server/
# Команда, которую нужно запустить после создания образа.
# Следующая команда означает запуск "python ./server.py".
command: python ./server.py
# Вспомните о том, что в качестве порта в 'server/server.py' указан порт 1234.
# Если мы хотим обратиться к серверу с нашего компьютера (находясь за пределами контейнера),
# мы должны организовать перенаправление этого порта на порт компьютера.
# Сделать это нам поможет ключевое слово 'ports'.
# При его использовании применяется следующая конструкция: [порт компьютера]:[порт контейнера]
# В нашем случае нужно использовать порт компьютера 1234 и организовать его связь с портом
# 1234 контейнера (так как именно на этот порт сервер
# ожидает поступления запросов).
ports:
- 1234:1234
# Второй сервис (контейнер): клиент.
# Этот сервис назван 'client'.
client:
# Здесь 'client/ соответствует пути к папке, которая содержит
# файл Dockerfile для клиентской части системы.
build: client/
# Команда, которую нужно запустить после создания образа.
# Следующая команда означает запуск "python ./client.py".
command: python ./client.py
# Ключевое слово 'network_mode' используется для описания типа сети.
# Тут мы указываем то, что контейнер может обращаться к 'localhost' компьютера.
network_mode: host
# Ключевое слово 'depends_on' позволяет указывать, должен ли сервис,
# прежде чем запуститься, ждать, когда будут готовы к работе другие сервисы.
# Нам нужно, чтобы сервис 'client' дождался бы готовности к работе сервиса 'server'.
depends_on:
- server
▍5. Сборка проекта
После того, как в docker-compose.yml
внесены все необходимые инструкции, проект нужно собрать. Этот шаг нашей работы напоминает использование команды docker build
, но соответствующая команда имеет отношение к нескольким сервисам:
$ docker-compose build
▍6. Запуск проекта
Теперь, когда проект собран, пришло время его запустить. Этот шаг нашей работы соответствует шагу, на котором, при работе с отдельными контейнерами, выполняется команда docker run
:
$ docker-compose up
После выполнения этой команды в терминале должен появиться текст, загруженный клиентом с сервера: Docker-Compose is magic!
.
Как уже было сказано, сервер использует порт компьютера 1234
для обслуживания запросов клиента. Поэтому, если перейти в браузере по адресу http://localhost:1234/, в нём будет отображена страница с текстом Docker-Compose is magic!
.
Полезные команды
Рассмотрим некоторые команды, которые могут вам пригодиться при работе с Docker Compose.
Эта команда позволяет останавливать и удалять контейнеры и другие ресурсы, созданные командой docker-compose up
:
$ docker-compose down
Эта команда выводит журналы сервисов:
$ docker-compose logs -f [service name]
Например, в нашем проекте её можно использовать в таком виде: $ docker-compose logs -f [service name]
.
С помощью такой команды можно вывести список контейнеров:
$ docker-compose ps
Данная команда позволяет выполнить команду в выполняющемся контейнере:
$ docker-compose exec [service name] [command]
Например, она может выглядеть так: docker-compose exec server ls
.
Такая команда позволяет вывести список образов:
$ docker-compose images
Итоги
Мы рассмотрели основы работы с технологией Docker Compose, знание которых позволит вам пользоваться этой технологией и, при желании, приступить к её более глубокому изучению. Вот репозиторий с кодом проекта, который мы здесь рассматривали.
Уважаемые читатели! Пользуетесь ли вы Docker Compose в своих проектах?
Docker — один из самых известных инструментов по работе с контейнерами. В статье мы расскажем, что такое контейнеры, где они применяются и чем могут быть полезны.
Managed Kubernetes помогает разворачивать контейнерные приложения в инфраструктуре Selectel. Сосредоточьтесь на разработке, а мы займемся рутинными операциями по обеспечению работы вашего кластера Kubernetes.
В конце будет практическая часть: мы создадим небольшое приложение, обернем его в образ и запустим. Все действия будем показывать на примере виртуальной машины облачной платформы Selectel.
Контейнеры — хорошая альтернатива аппаратной виртуализации. Они позволяют запускать приложения в изолированном окружении, но при этом потребляют намного меньше ресурсов.
В первую очередь эта статья будет полезна тем, кто вообще не знаком с контейнерами или Docker. Мы расскажем самые базовые вещи, а наш пример по созданию приложения будет довольно простым. Но это позволит вам понять основы Docker и затем двигаться дальше — изучать более сложные материалы.
Что такое контейнеры
Прежде чем рассказывать про Docker, нужно сказать несколько слов о технологии контейнеризации.
Контейнеры — это способ стандартизации развертки приложения и отделения его от общей инфраструктуры. Экземпляр приложения запускается в изолированной среде, не влияющей на основную операционную систему.
Разработчикам не нужно задумываться, в каком окружении будет работать их приложение, будут ли там нужные настройки и зависимости. Они просто создают приложение, упаковывают все зависимости и настройки в некоторый единый образ. Затем этот образ можно запускать на других системах, не беспокоясь, что приложение не запустится.
Docker — это платформа для разработки, доставки и запуска контейнерных приложений. Docker позволяет создавать контейнеры, автоматизировать их запуск и развертывание, управляет жизненным циклом. Он позволяет запускать множество контейнеров на одной хост-машине.
Контейнеризация похоже на виртуализацию, но это не одно и то же. Виртуализация запускает полноценный хост на гипервизоре со своим виртуальным оборудованием и операционной системой. При этом внутри одной ОС можно запустить другую ОС. В случае контейнеризации процесс запускается прямо из ядра основной операционной системы и не виртуализирует оборудование. Это означает, что контейнеризованное приложение может работать только в той же ОС, что и основная. Контейнеры не виртуализируют оборудование, поэтому потребляют меньше ресурсов.
Контейнеры упрощают работу как программистам, так и администраторам, которые развертывают эти приложения.
Docker решает проблемы зависимостей и рабочего окружения
Контейнеры позволяют упаковать в единый образ приложение и все его зависимости: библиотеки, системные утилиты и файлы настройки. Это упрощает перенос приложения на другую инфраструктуру.
Например, разработчики создают приложение в системе разработки — там все настроено, приложение работает. Когда оно готово, его нужно перенести в систему тестирования, а затем в продуктивную среду. Если в одной из них нет нужной зависимости, приложение не будет работать. Программистам придется отвлечься от разработки и совместно с командой поддержки разобраться в ситуации.
В контейнерах такой проблемы нет, так как они содержат в себе все необходимое для запуска приложения. Специалисты занимаются разработкой, а не решением инфраструктурных проблем.
Изоляция и безопасность
Контейнер — это набор процессов, изолированных от основной операционной системы. Приложения работают только внутри контейнеров и не имеют доступа к основной операционной системе. Это повышает безопасность приложений:они не смогут случайно или умышленно навредить основной системе. Если приложение в контейнере завершится с ошибкой или зависнет, это никак не затронет основную ОС.
Ускорение и автоматизация развертывания приложений и масштабируемость
Контейнеры упрощают развертывание приложений. В классическом подходе для установки программы нужно совершить несколько действий: выполнить скрипт, изменить файлы настроек и так далее. В этом процессе не исключена вероятность человеческой ошибки: пользователь запустит скрипт два раза, перепутает последовательность или что-то не поймет. Контейнеры позволяют полностью автоматизировать этот процесс, так как включают в себя все нужные зависимости и порядок выполнения действий.
Также контейнеры упрощают развертывание на нескольких серверах. В классическом подходе для того, чтобы развернуть одно и то же приложение на нескольких машинах, нужно будет повторять одни и те же действия. Контейнеры избавляют от этой рутинной работы и позволяют автоматизировать развертывание.
Контейнеры приближают к микросервисной архитектуре
Контейнеры хорошо вписываются в микросервисную архитектуру. Это подход к разработке, при котором приложение разбивается на небольшие компоненты, по возможности независимые. Обычно противопоставляется монолитной архитектуре, где все части системы сильно связаны друг с другом.
Это позволяет разрабатывать новую функциональность быстрее, ведь в случае с монолитной архитектурой изменение какой-то части может затронуть всю остальную систему.
Docker compose — одновременно развернуть несколько контейнеров
Docker-compose позволяет разворачивать и настраивать несколько контейнеров одновременно. Например, для веб-приложения нужно развернуть стек LAMP: Linux + Apache, MySQL, PHP. Каждое из приложений — это отдельный контейнер для ОС Linux. Но в этой ситуации нам нужны именно все контейнеры вместе, а не отдельно взятое приложение. Docker-compose позволяет развернуть и настроить все приложения одной командой, а без него пришлось бы разворачивать и настраивать каждый контейнер отдельно.
Создайте кластер любой конфигурации в несколько кликов
Упростите процесс развертывания, масштабирования и обслуживания контейнерной инфраструктуры с Managed Kubernetes.
Тестировать сервис
Хранение данных в Docker
Одна из главных особенностей контейнеров — эфемерность. Это означает, что контейнеры могут быть в любой момент остановлены, перезапущены или уничтожены. При этом все накопленные данные в контейнере будут потеряны. Поэтому приложения нужно разрабатывать так, чтобы они не полагались на хранилище данных в контейнере, это называется принципом Stateless.
Это хорошо подходит для приложений или сервисов, которые не сохраняют результаты своей работы. Например, функции расчета или преобразования данных: им на вход поступил один набор данных, они его преобразовали или рассчитали и вернули результат. Все, ничего никуда сохранять не нужно.
Но далеко не все приложения такие, и есть много данных, которые нужно сохранить. В контейнерах для этого предусмотрены несколько способов.
Тома (Docker volumes)
Это способ, при котором Docker сам создает директории для хранения данных. Их можно сделать доступными для разных контейнеров, чтобы они могли обмениваться данными. По умолчанию эти директории создаются на хост-машине, но можно использовать и удаленные хранилища: файловый сервер или объектное хранилище.
Монтирование каталога (bind mount)
В этом случае директория сначала создается на хост-машине а уже потом монтируется в контейнеры.
Но этот способ не рекомендуется, потому что он усложняет резервное копирование, миграцию и совместное использование данных несколькими контейнерами.
Архитектура (компоненты) Docker
Теперь расскажем подробнее про компоненты, из которых состоит Docker.
Docker daemon
Это некоторый резидентный процесс, который запущен на хост-машине постоянно. Он владеет всей инфраструктурой, а также предоставляет интерфейс взаимодействия с контейнерами, включающего создание и удаление, запуск и остановку.
В ранних версиях платформы Docker можно встретить упоминание о dockerd, но на текущий момент демоны уже успели разбиться на отдельные проекты. Все чаще можно встретить его современника — containerd.
Docker client (клиент)
Это интерфейс командной строки для управления Docker daemon. Мы пользуемся этим клиентом, когда создаем и разворачиваем контейнеры, а клиент отправляет эти запросы в Docker daemon.
Docker image (образ)
Это неизменяемый файл (образ), из которого разворачиваются контейнеры. Приложения упаковываются именно в образы, из которых потом уже создаются контейнеры. В технической литературе можно также встретить описание image как шаблона запуска процесса.
Приведем аналогию на примере установки операционной системы. В дистрибутиве (образе) ОС есть все, что необходимо для ее установки. Но этот образ нельзя запустить, для начала его нужно «развернуть» в готовую ОС. Так вот, дистрибутив для установки ОС — это образ, а установленная и работающая ОС — это контейнер. Но контейнеры обычно разворачиваются одной командой — это намного проще и быстрее, чем установка ОС.
Docker container (контейнер)
Это уже развернутое из образа и работающее приложение.
Docker Registry
Это репозиторий с образами. Разработчики создают образы своих программ и выкладывают их в репозиторий, чтобы их можно было скачать и воспользоваться ими. Распространенный публичный репозиторий — Docker Hub. В нем собраны образы множества популярных программ или платформ: базы данных, веб-серверы, компиляторы, операционные системы и так далее. Также можно создать свой приватный репозиторий, например внутри компании. Разработчики будут размещать там образы, которые будут использоваться всей компанией.
Dockerfile
Dockerfile — это инструкция для сборки образа. Это простой текстовый файл, содержащий по одной команде в каждой строке. В нем указываются все программы, зависимости и образы, которые нужны для разворачивания образа.
Для примера рассмотрим Dockerfile, который мы будем использовать далее в этой статье чтобы развернуть собственное приложение:
FROM python:3
COPY main.py /
CMD [ "python", "./main.py" ]
Первая строчка означает, что за основу мы берем образ с названием python версии 3 это называется базовый образ. Docker найдет его в docker registry, скачает и будет использовать за основу. Вторая строчка означает, что нужно скопировать файл main.py в корень файловой системы контейнера. Третья строчка означает, что нужно запустить python и передать ему в качестве параметра название файла main.py.
Далее рассмотрим примеры нескольких команд докер и что происходит, когда мы их выполняем.
Все эти команды выполняются в Docker client, который отправляет их в Docker daemon:
- Команда docker build (зеленая стрелка) читает dockerfile и собирает образ.
- Команда docker pull (красная стрелка) скачивает образ из docker registry. По умолчанию docker скачивает образы из публичного репозитория Docker Hub. Но можно создать свой репозиторий и настроить докер, чтобы он работал с ним.
- Команда docker run (черная стрелка) берет образ и запускает из него контейнер.
Создаем виртуальную машину для работы с Docker
Перейдем к практической части. Мы установим докер, создадим приложение, обернем его в контейнер и запустим. Мы для примера будем использовать виртуальную машину на платформе Selectel.
В панели управления заходим в раздел «Облачная платформа» — «Серверы», нажимаем кнопку «Создать сервер».
На следующем экране выбираем параметры сервера: имя, регион, ОС, параметры производительности и так далее. Сейчас для нас важны параметры «Источник» — выбираем ОС Ubuntu 20.04 и «Конфигурация» — выбираем 2 vCPU и 8 ГБ оперативной памяти.
Далее обратите внимание на разделы Сеть и Доступ. В разделе Сеть нужно выбрать подсеть с публичным адресом, чтобы к виртуальной машине можно было подключаться из интернета. В разделе Доступ будет указан пароль для root-пользователя, а также необходимо загрузить SSH-ключ, чтобы подключаться к виртуальной машине. Подробную инструкцию о подключении смотрите в базе знаний.
После этого внизу страницы нажимаем кнопку «Создать». Виртуальная машина создается за несколько минут, и после того, как она перейдет в статус ACTIVE, к ней можно подключаться по SSH.
Установка Docker
Мы рассмотрим установку докера на примере Ubuntu. Если у вас другой дистрибутив Linux или операционная система — ищите соответствующую инструкцию на официальном сайте.
Для начала синхронизируем пакетную базу apt и установим нужные зависимости:
sudo apt-get update
sudo apt-get install
apt-transport-https
ca-certificates
curl
gnupg
lsb-release
Далее импортируем GPG-ключ для репозитория docker:
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
Теперь добавим новый репозиторий в список apt:
echo
"deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
Теперь можно устанавливать докер:
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io
По умолчанию, доступ к docker daemon есть только у пользователя root. Чтобы с докером могли работать и другие пользователи, их нужно добавить в специальную группу — docker. Выполните эту команду из под обычного пользователя:
sudo usermod -aG docker $USER
После этого необходимо перелогиниться, чтобы изменение вступило в силу.
Запуск контейнера
Теперь попробуем запустить какое-нибудь готовое приложение. Выполните команду:
docker run ubuntu echo 'hello from ubuntu'
Команда docker run создает и запускает контейнер из образа. В этом примере мы создаем контейнер из образа ubuntu, затем выполняем в нем команду echo ‘hello from ubuntu’. Но так как у нас чистая установка докера и мы не скачали ни одного образа, докер сначала найдет этот образ в публичном репозитории Docker Hub, скачает, а потом создаст из него контейнер. В следующий раз, когда нам понадобится образ ubuntu, докер уже не будет его скачивать.
После выполнения команды в терминале появится строка hello from ubuntu, и контейнер сразу остановится. Теперь выполним другую команду:
docker run -it ubuntu
Эта команда запустит контейнер в интерактивном режиме, то есть контейнер запустится и будет ждать дальнейших команд. При этом мы окажемся внутри операционной системы контейнера: запустится оболочка (bash), и мы сможем выполнять какие-то команды внутри контейнера. Чтобы выйти из контейнера, введите команду exit.
Создание собственного образа и запуск контейнера
Теперь создадим HelloWorld-приложение на Python, обернем его в образ и запустим.
Для начала создадим директорию, в которой мы будем работать и перейдем в нее:
mkdir first-docker-app
cd first-docker-app
Создадим файл main.py и запишем в него одну строчку кода:
echo 'print("Hello from python");' >> main.py
Проверим, что наша программа работает. Для этого выполним команду:
python main.py
В консоли должно выйти сообщение Hello from python. Это и есть наше простое приложение. Теперь нужно обернуть его в докер-образ. Для этого создадим файл Dockerfile и напишем в нем три строчки:
FROM python:3
COPY main.py /
CMD [ "python", "./main.py" ]
В первой строке мы указываем образ, который берем за основу. Так как мы пишем приложение на Python, нужно чтобы в нашем образе он уже был установлен. Самый простой способ это сделать — использовать готовый официальный образ с Docker Hub. Цифра 3 — это тег. Он означает, что нужно использовать третью версию Python. Вместо этого можно было бы использовать тег latest, который означает самую последнюю версию, или можно было указать номер конкретной версии, например 3.8.8.
Во второй строчке мы копируем наш файл main.py в корневую директорию образа.
Третья строчка — запускаем python и передаем ему в качестве параметра имя нашего файла.
Теперь из этого докер-файла можно собирать образ. Выполним команду:
docker build -t first-docker-app .
Параметр -t обозначает имя нашего образа, мы назвали его first-docker-app.
Так как у нас еще нет скачанного образа python, то докер сам скачает его из Docker Hub и затем будет использовать его в качестве основы для создания нашего образа.
Проверим список установленных у нас образов:
docker images
Мы увидим, что у нас установлено три образа:
REPOSITORY TAG IMAGE ID CREATED SIZE
first-docker-app latest 649cceb4dfd2 4 seconds ago 885MB
python 3 b1aa63f57d3c 2 days ago 885MB
ubuntu latest 8e428cff54c8 4 days ago 72.9MB
first-docker-app — это наш образ, который мы только что создали. python — это образ python, который докер автоматически скачал чтобы собрать наш образ. ubuntu — образ, который мы пробовали для запуска готового приложения.
Теперь создадим контейнер из нашего образа и запустим его:
docker run first-docker-app
В результате нам выведется результат: Hello from python.
Итог: Мы создали свое приложение, упаковали его в докер-образ и запустили. Конечно, это очень простой пример. Наша программа состоит всего из одной строчки, а dockerfile из трех. Но это позволяет понять базовые принципы работы докера, как он устроен, как создавать свои образы и запускать контейнеры.
Заботимся о работе и доступности вашего кластера даже в пиковые нагрузки
Managed Kubernetes — это готовый сервис Selectel. Мы отвечаем за автоматическое обновление кластера, несем ответственность по SLA за его доступность и бесперебойную работу Control Plane.
Создать кластер
Список полезных команд
Теперь приведем список полезных команд, которые могут пригодиться при работе с докером.
Посмотреть список всех контейнеров
Эта команда выведет список всех докер контейнеров:
docker ps
Но по умолчанию выводятся только работающие контейнеры. Чтобы вывести все, в том числе и остановленные, используйте опцию -a:
docker ps -a
Остановить и удалить все докер контейнеры
Чтобы удалить контейнеры, сначала их нужно остановить. Первая команда остановит запущенные контейнеры, если они есть. А вторая команда — удалит их.
docker stop $(docker ps -a -q)
docker rm $(docker ps -a -q)
Запустить контейнер с последующим удалением
По умолчанию контейнеры после завершения работы останавливаются, но не удаляются. Они сохраняют свое состояние и при необходимости их можно запустить снова. Чтобы контейнер удалялся сразу после остановки, добавьте к команде docker run параметр —rm, например:
docker run --rm ubuntu echo 'hello from ubuntu'
Посмотреть список всех скачанных образов
docker images
Удалить докер образ
docker rmi <имя-образа>
Если у этого образа есть контейнеры, пусть даже остановленные, докер не позволит его удалить. Он выдаст сообщение:
unable to remove repository reference <имя-образа> (must force) - container <id-контейнера> is using its
Чтобы принудительно удалить образ, добавьте флаг -f:
docker rmi -f <имя-образа>
Получить список всех контейнеров, созданных из определенного образа
docker ps -a --filter ancestor=<название-образа>
Итог
В статье мы рассмотрели, что такое контейнеры и Docker, как они работают и чем отличаются от виртуализации. Также мы создали простое python-приложение, обернули его в образ и запустили контейнер.
Мы рассказали основы технологий, но не затронули более сложные темы, вроде Docker Swarm, настройку сети или настройки процессов CI/CD. Но этого вполне достаточно, чтобы погрузиться в основы технологий.
В этом гайде разбираемся, для чего нужен Docker и Docker Compose, что такое контейнеризация и Docker-образы, а также как развернуть простое веб-приложение с использованием PHP-FPM, Nginx и Postgres.
- Что такое Docker
- Как работает Docker
- Как создать свой Docker-образ
- Что такое Docker Compose и как он работает
- Как создать простое веб-приложение с помощью Docker
- Итог
Давайте представим себе разработчика, который приходит на работу в новую компанию. На онбординге тимлид дает ему первое шуточное задание: изучить сайт организации и найти на нем страницу с фотографиями котиков.
Разработчик узнаёт, что сайт компании работает с помощью веб-сервера Nginx, менеджера процессов PHP-FPM и системы управления базами данных Postgres. Теперь программист ищет нужную страницу. Поиск выглядит так:
- Разработчик вводит в браузере адрес сайта
- Браузер запрашивает HTML-страницу с котиками по указанному адресу
- HTTP-сервер Nginx принимает запрос и делегирует создание страницы PHP-FPM
- PHP-FPM запрашивает данные о котиках из базы Postgres, строит HTML-страницу и отдает обратно его серверу Nginx, а тот — клиенту-браузеру
- Разработчик видит страницу с котиками.
Свое первое задание разработчик выполняет на компьютере тимлида, где уже установлен Nginx, PHP-FPM и Postgres. На следующий день ему выдают новый компьютер, на котором этих программ нет.
Чтобы начать работать над сайтом вместе с коллегами, разработчик разворачивает проект, то есть устанавливает и настраивает все необходимое для работы. Он делает это последовательно:
- Устанавливает Nginx
- Устанавливает PHP-FPM и все нужные расширения
- Настраивает совместную работу Nginx и PHP-FPM
- Устанавливает Postgres, создает пользователей, нужные базы и схемы.
Установка идет долго: приходится ждать, пока сначала установится одна программа, потом другая. Сложности добавляет и то, что вся его команда работает над проектом на разных операционных системах: одни на macOS, а другие на Ubuntu или Windows.
Чтобы не терять время, устанавливая программу за программой, разработчик мог бы автоматизировать свои действия с помощью программы Docker. Она разворачивает проект программиста за считанные минуты.
Что такое Docker
Docker — это популярная программа, в основе которой лежит технология контейнеризации. Docker позволяет запускать Docker-контейнеры с приложениями из заранее заготовленных шаблонов — Docker-образов (или по-другому Docker images).
Контейнеризация — это технология, которая помогает запускать приложения изолированно от операционной системы. Приложение как бы упаковывается в специальную оболочку — контейнер, внутри которого находится среда, необходимая для работы.
Простыми словами контейнер — это некая изолированная песочница для запуска ваших приложений.
На картинке видно, что приложение 1 и приложение 2 изолированы как друг от друга, так и от операционной системы.
Что еще может делать Docker:
- Управлять изолированными приложениями
- Ускорять и автоматизировать развертывание приложений
- Доставлять приложения до серверов
- Масштабировать приложения
- Запускать на одном компьютере разные версии одной программы.
Читайте также:
Как читать чужой код: 6 правил, которые стоит помнить разработчику
Как работает Docker
Концепцию программы легче понять на практике. Сначала установим на компьютер Docker и запустим HTTP-сервер Nginx. Для этого введем следующую команду:
docker run -p 8080:80 nginx:latest
Далее откроем браузер и забьем в адресную строку: 127.0.0.1:8080
. Откроется страница приветствия Nginx.
Теперь разберемся подробнее, что происходит, когда мы вводим команду docker run -p 8080:80 nginx:latest
. Она выполняет следующее:
- Скачивает docker-образ — шаблон для создания Docker-контейнера —
nginx:latest
из публичного репозитория Docker Hub (если его не скачивали ранее). Docker-образ содержит все необходимое для запуска приложения: код, среду выполнения, библиотеки, переменные окружения и файлы конфигурации. На странице Nginx в Docker Hub можно найти Docker-образ nginx:latest, где latest — это тег (метка, снимок), который ссылается на самый свежий docker-образ и описывает его. - Запускает Docker-контейнер с помощью Docker-образа
- Пробрасывает порт. Ранее мы объясняли, что процессы в Docker-контейнерах запускаются в изоляции от ОС, то есть все порты между ОС и Docker-контейнером закрыты. Для того, чтобы мы смогли обратиться к Nginx, нужно пробросить порт, что и делает опция
-p 8080:80
, где 80 — это порт Nginx внутри контейнера, а 8080 — порт в локальной сети ОС.
Как создать свой Docker-образ
Теперь попробуем создать свой Docker-образ, взяв за основу nginx:latest
. Docker умеет создавать Docker-образ, читая текстовые команды, которые записаны в файл Dockerfile.
Вот пример простейшего Dockerfile:
FROM nginx:latest
RUN echo 'Hi, we are building a custom docker image from nginx:latest!'
COPY nginx-custom-welcome-page.html /usr/share/nginx/html/index.html
Команда FROM
задает базовый (родительский) Docker-образ и всегда вызывается в первую очередь. Команда COPY
копирует файлы в Docker-контейнер.
С помощью COPY
можно заменить стандартную велком-страницу Nginx на такую страницу:
<!DOCTYPE html>
<html>
<body>
<h1>Welcome to custom Nginx page!</h1>
</body>
</html>
Узнать подробнее об этих и других командах Docker можно в официальной документации.
Теперь, когда мы разобрались, за что отвечают команды, создадим Docker-образ из Dockerfile:
$ docker build -t nginx_custom:latest -f /opt/src/docker-for-kids/dockerFiles/Nginx-custom/Dockerfile /opt/src/docker-for-kids
Sending build context to Docker daemon 139.3kB
Step 1/3 : FROM nginx:latest
latest: Pulling from library/nginx
31b3f1ad4ce1: Pull complete
fd42b079d0f8: Pull complete
30585fbbebc6: Pull complete
18f4ffdd25f4: Pull complete
9dc932c8fba2: Pull complete
600c24b8ba39: Pull complete
Digest: sha256:0b970013351304af46f322da1263516b188318682b2ab1091862497591189ff1
Status: Downloaded newer image **for** nginx:latest
---**>** 2d389e545974
Step 2/3 : RUN echo 'Hi, we are building a custom docker image from nginx:latest!'
---**>** Running **in** 05ffd060061f
Hi, we are building a custom docker image from nginx:latest!
Removing intermediate container 05ffd060061f
---**>** 9ac62be4252a
Step 3/3 : COPY nginx-custom-welcome-page.html /usr/share/nginx/html/index.html
---**>** 704121601a45
Successfully built 704121601a45
Successfully tagged nginx_custom:latest
Поясним, какие команды мы использовали в этом коде:
-t nginx_custom:latest
— это имя будущего Docker-образа,latest
— это тег-f /opt/src/docker-for-kids/dockerFiles/Nginx-custom/Dockerfile
— путь до Dockerfile/opt/src/docker-for-kids
— директория, в контексте которой будет создан Docker-образ. Контекст — это все то, что доступно для команд из Dockerfile при сборке (билде) образа. Процесс создания Docker-образа может ссылаться на любой из файлов в контексте.
Теперь запускаем команду:
$ docker run -p 8080:80 Nginx_custom:latest
Docker-образ готов.
Читайте также:
Как настроить VS Code для разработки на PHP и JavaScript
Что такое Docker Compose и как он работает
С ростом количества Docker-контейнеров их становится труднее поддерживать. Конфигурация каждого контейнера описывается в своем Dockerfile, и их нужно запускать отдельной командой. Это же касается сборки или пересборки контейнеров.
Работу облегчает Docker Compose — это инструмент для описания многоконтейнерных приложений. С его помощью можно собрать один файл, в котором наглядно описываются все контейнеры. Еще Docker Compose позволяет собирать, останавливать и запускать файлы одной командой.
Для описания приложений используется YAML-файл.
version: '3'
services:
nginx:
container_name: nginx-test # имя Docker-контейнера
build: # создать Docker-образ из DockerFile
context: . # путь, в контексте которого будет создан Docker-образ
dockerfile: ./dockerFiles/nginx/Dockerfile # путь до Dockerfile, из которого будет собран Docker-образ
ports: # проброс портов
- "80:80"
networks: # имя сети, к которой будет подключен Docker-контейнер
- test-network
depends_on: # эта программа будет запущена только после того, как запустится сервис под именем php-fpm
- php-fpm
volumes: # монтирование директорий, директория-на-хост-машине: директория-в-докере
- ./:/var/www/hello.dev/
php-fpm:
container_name: php-fpm-test
build:
context: .
dockerfile: ./dockerFiles/php-fpm/Dockerfile
networks:
- test-network
volumes:
- ./:/var/www/hello.dev/
postgres:
container_name: postgres-test
image: postgres:14.1-alpine # тег Docker-образа из https://hub.docker.com/
environment:
postgres_PASSWORD: mysecretpass # переменные окружения, которые использует Docker-контейнер
networks:
- test-network
networks: # явно объявленные сети
test-network:
driver: bridge
Если изображать этот код схематично, то описание приложения выглядит так:
Каждый сервис находится внутри Docker-контейнера. Точкой входа в приложение, как и в случае с тем разработчиком и веб-сайтом компании, является Nginx. Пользователи веб-сайта делают запросы к Nginx, у которого проброшен порт 80.
Разберем еще несколько команд, которые реализует Docker:
network
. Как мы объяснили ранее, каждое приложение в Docker-контейнере находится в изоляции.networks
объединяет все Docker-контейнеры в одну сеть с именем test-network, и это позволяет обращаться к нужному контейнеру по его имени.volumes
— это механизм для хранения данных вне Docker-контейнера, то есть в файловой системе нашей ОС.volumes
решает проблему совместного использования файлов.
Все примеры, а также исходники Dockerfile можно взять из репозитория на GitHub.
Как создать простое веб-приложение с помощью Docker
Создадим простое веб-приложение, которое покажет нам сообщение об успешном подключении к базе данных. Вместо адреса базы данных используем host=postgres
, такое же имя cервиса, как и в YAML-файле. Напомню, что эта возможность появилась благодаря общей сети test-network.
index.php
<?php
try {
$pdo = new PDO("pgsql:host=postgres;dbname=postgres", 'postgres', 'mysecretpass');
echo "Подключение к базе данных установлено! <br>";
return;
} catch (PDOException $exception) {
echo "Ошибка при подключении к базе данных<br><b>{$exception->getMessage()}</b><br>";
}
PDO
— это интерфейс для доступа к базам данных в PHP. Подробнее об этом можно узнать в официальной документации.
Теперь, чтобы создать все Docker-образы и запустить Docker-контейнеры нужно выполнить:
docker-compose up --build
Выполняем index.php и видим успешное соединение с базой данных.
Веб-приложение для самостоятельного запуска можно найти в репозитории на GitHub.
Итог
Освоив Docker, разработчики могут разворачивать все необходимые им сервисы на каком угодно компьютере. Также эта программа — отличный инструмент для быстрой доставки до серверов, тестирования. Изучить Docker не так тяжело, как может показаться новичкам, но зато это умение значительно сэкономит их время на ручной установке софта. Почитать про Docker подробнее можно на официальном сайте.
Изучите основы Docker:
На Хекслете есть курс по основам Docker. Пройдите его, чтобы подробнее узнать об этой программе, научиться работать с образами, управлять контейнерами и получить поддержку от менторов и единомышленников.
Изучить Docker
На дворе закат 2022-го, и большая часть IT-индустрии только и делает, что работает с контейнерами. Откуда они появились, как добились глобального признания и при чём тут Docker? Расскажет разработчица в команде инфраструктуры Яндекса, действующий автор курса «DevOps для эксплуатации и разработки» Дарья Меленцова.
- Начнём с основ
- Благодаря каким механизмам работает Docker
- Терминология
- Запуск и начальная настройка Docker
- Развёртывание веб-приложения
- Создание Docker Image
- Выводы
- Дополнительные материалы по Docker
Дарья Меленцова
разработчица в команде инфраструктуры Яндекса, действующий автор курса «DevOps для эксплуатации и разработки»
Из этой статьи вы узнаете:
- что такое Docker и его главные возможности;
- почему Docker стал де-факто современной индустрией программного обеспечения;
- как создавать и развёртывать Docker-контейнеры.
Начнём с основ
Что такое Docker
Разработчики Docker дают ему такое определение: «Docker helps developers bring their ideas to life by conquering the complexity of app development», что можно перевести как «Docker помогает разработчикам воплощать свои идеи в жизнь, преодолевая сложность разработки приложений». Звучит многообещающе, не правда ли?
Если конкретнее, Docker — это инструмент, с помощью которого разработчики, системные администраторы и все желающие могут легко запускать разные приложения в изолированных контейнерах на одном сервере.
Контейнеры не знают, что рядом развёрнуты другие контейнеры с приложениями, они полностью изолированы друг от друга. В каждом контейнере можно настроить окружение, необходимое именно для этого приложения.
В отличие от виртуальных машин, контейнеры не требуют серьёзных мощностей, что позволяет более эффективно использовать ресурсы сервера.
Что такое контейнер
Ещё недавно приложения разворачивали на физических серверах, поэтому возникали сложности, когда это нужно было сделать быстро.
- Все серверы настраивались вручную (или почти вручную). Подключение сервера, установка ОС, настройка правильного окружения, сети и других параметров занимали много времени.
- Были проблемы с гибким масштабированием. Представьте, что у вас на сервере развёрнут интернет-магазин. В обычное время приложение справляется с потоком пользователей, но в канун Нового года аудитория возрастает, ведь все хотят закупиться подарками. И тут оказывается, что интернет-магазин не справляется с нагрузкой и надо либо добавить ресурсы на сервер, либо поднять ещё несколько экземпляров сервиса. Да, мы можем заранее подумать о празднике и предвидеть наплыв покупателей, но что делать с теми ресурсами, которые будут простаивать после Нового года?
- Требовалось эффективнее использовать ресурсы. Если на большом и мощном физическом сервере разместить какое-нибудь скромное приложение, которому нужно от силы 20% всех мощностей, что делать с остальным запасом? Может быть, подселить к этому приложению ещё одно или несколько? Казалось бы, вариант, пока вы не узнаете, что для работы приложений нужны разные версии одного и того же пакета.
Программисты — умные и творческие люди, поэтому они начали думать, как можно избежать этих сложностей. Так родилась виртуализация!
Виртуализация — технология, которая позволяет создавать виртуальное представление ресурсов отдельно от аппаратных. Например, под операционную систему (далее — ОС) можно отдать не весь диск, а только часть, создав его виртуальное представление.
Есть много разных видов виртуализации, и один из них — аппаратная виртуализация.
Аппаратная виртуализация
Идея в том, чтобы взять сервер и разделить его на кусочки. Допустим, у вас есть сервер, на котором установлена хостовая ОС, и внутри неё запускаются виртуальные машины (далее — ВМ) с гостевыми ОС. Между хостовой ОС и ВМ есть прослойка — гипервизор, который управляет разделением ресурсов, а также изоляцией гостевых ОС.
У аппаратной виртуализации есть большой плюс: внутри ВМ можно запускать абсолютно разные ОС, отличные от хостовой, но ценой дополнительных расходов на гипервизор.
Казалось бы, проблемы с утилизацией ресурсов и изоляцией приложений решены, но как быть с установкой ОС и настройкой окружения: всё ещё делаем вручную и на каждой ВМ? Да и зачем платить за гипервизор, если не нужно держать на одном сервере Windows и Linux — достаточно ядра хостовой ОС?
На этот случай придумали контейнерную виртуализацию. При таком типе виртуализация происходит на уровне ОС: есть хостовая ОС и специальные механизмы, которые позволяют создавать изолированные контейнеры. В роли гипервизора выступает хостовая ОС — она отвечает за разделение ресурсов между контейнерами и обеспечивает их изолированность.
Контейнерная виртуализация
Контейнер — это изолированный процесс, который использует основное ядро ОС. Работа с контейнерами помогает решить следующие проблемы:
- утилизации ресурсов (на одном сервере можно запустить несколько контейнеров);
- изоляции приложений;
- установки ОС (по сути, мы используем хостовую ОС);
- настройки окружения для приложения (можно один раз настроить окружение и быстро клонировать его между контейнерами).
Почему контейнеры и Docker
Как мы уже знаем, контейнер — это изолированный процесс, который работает со своим кусочком файловой системы, памятью, ядром и другими ресурсами. При этом он думает, что все ресурсы принадлежат только ему.
Все механизмы для создания контейнеров заложены в ядро Linux, но на практике обычно используют готовые среды выполнения вроде Docker, containerd и cri-o, которые помогают автоматизировать развёртывание и управление контейнерами.
Особенности контейнеров:
- Короткий жизненный цикл. Любой контейнер можно остановить, перезапустить или удалить. Данные, которые содержатся в контейнере, тоже пропадут. Поэтому при проектировании приложений, которые подходят для контейнеризации, используют правило: не хранить важные данные в контейнере. Такой подход проектирования называют Stateless.
- Контейнеры маленькие и лёгкие, их объём измеряется в мегабайтах. Так получается, потому что в контейнер упаковывают лишь те процессы и зависимости ОС, которые необходимы для приложения. Легковесные контейнеры занимают мало места на диске и быстро запускаются.
- Контейнеризация обеспечивает изоляцию процессов. Приложения, которые работают внутри контейнера, не имеют доступа к основной ОС.
- Благодаря контейнерам можно перейти с монолита на микросервисную архитектуру.
- Не нужно тратиться на гипервизор, и можно запустить больше контейнеров, чем ВМ на одних и тех же ресурсах.
- Контейнеры хранятся в специальных репозиториях, и каждый контейнер содержит всё необходимое окружение для запуска приложения, благодаря чему можно автоматизировать развёртывание приложения на разных хостах.
Теперь обсудим, какие преимущества даёт Docker.
- Сообщество. Существует огромное хранилище контейнеров с открытым исходным кодом, и вы можете скачать готовый образ для конкретной задачи.
- Гибкость. Docker позволяет создавать базовые шаблоны контейнеров (image) и использовать их повторно на различных хостах. Docker-контейнеры можно легко запустить как на локальном устройстве, так и в любой облачной инфраструктуре.
- Скорость развёртывания. Шаблон контейнера содержит всё необходимое окружение и настройки для работы приложения, нам не нужно настраивать всё это каждый раз с нуля.
- Нет проблемы с зависимостями и версиями пакетов. Docker позволяет упаковывать различные языки программирования и стек технологий в контейнер, чем избавляет от проблемы несовместимости разных библиотек и технологий в рамках одного хоста.
Как вы уже знаете, в ядре Linux из коробки есть все необходимые механизмы для создания контейнеров:
- capabilities — позволяет выдать процессу часть расширенных прав, которые доступны только
root
. Например, разрешить удалять чужие файлы, завершать другие процессы (командаkill
) или изменять атрибуты у файлов (командаchown
); - namespace — это абстракция в Linux, с помощью которой можно создавать своё изолированное окружение в ОС. То есть такую коробочку, в которой свои пользователи, своя сеть, свои процессы и всё остальное. При этом изменения в namespace видны только членам этого namespace. Есть шесть типов пространств имён (namespaces): IPC, Network, Mount, PID, User, UTS.
Например:
- Network namespace отвечает за ресурсы, связанные с сетью. У каждого namespace будут свои сетевые интерфейсы, свои таблицы маршрутизации.
- User namespace специализируется на пользователях и группах в рамках namespace.
- PID namespace заведует набором ID процессов. Первый процесс, созданный в новом namespace, имеет
PID = 1
, а дочерним процессам назначаются следующие PID.
- cgroup объединяет несколько процессов в группу и управляет ресурсами для этой группы.
Традиционно лимиты в Linux можно задавать для одного процесса, и это неудобно: вы могли задать какому-то процессу не больше n мегабайт памяти, но как указывать лимиты на приложение, если у него больше одного процесса? Поэтому появились cgroups
, позволяющие объединить процессы в группу и навесить на неё лимиты.
Давайте разберёмся, как Docker создаёт контейнер из capabilities, namespace и cgroup.
Docker — это очень тонкая прослойка вокруг ядра. Он создаёт контейнер на основе docker image c заданными настройками. Когда вы попросите Docker создать контейнер, он автоматически создаст набор namespaces и cgroup для этого контейнера.
PID Namespace нужны для того, чтобы процессы внутри контейнера не могли видеть другие процессы, которые работают в другом контейнере или на хостовой системе, и влиять на них.
Network namespace — контейнер получит свой сетевой стек, а значит, он не сможет получить доступ к сокетам или сетевым интерфейсам другого контейнера.
Аналогичная история со всеми остальными пространствами имён — для каждого контейнера своё дерево каталогов, хостнеймы и прочее.
При создании Docker-контейнера мы можем указать, сколько памяти или cpu выдать конкретному контейнеру, и ОС будет следить за этим лимитом. Такой контроль нужен, чтобы один контейнер случайно не убил всю систему, съев всю память или перегрузив процессор.
По умолчанию Docker при создании контейнера урезает все capabilites внутри него, оставляя только часть возможностей — смену атрибутов UID
и GID
(chown
), kill
, chroot
и несколько других. Это сделано в целях безопасности, чтобы злоумышленнику не достались все root-права, если бы он смог выбраться из контейнера.
Терминология
Прежде чем начать работу с Docker, нужно изучить несколько терминов.
Архитектура Docker
Docker Image
Образ — это шаблон для ваших будущих контейнеров. В образе описывается, что должно быть установлено в контейнере и какие действия нужно выполнить при старте контейнера.
В практической части вы будете использовать команду docker pull
, чтобы загрузить busybox image из специального хранилища Docker образов — docker hub.
Docker Container
Контейнер — это исполняемый экземпляр образа (image). Его можно создавать, запускать, останавливать и удалять. Также можно подключать к контейнеру хранилище, объединять контейнеры одной или несколькими сетями и общаться с контейнерами, используя Docker API или CLI.
Увидеть список запущенных контейнеров можно через команду docker ps
.
Docker Daemon
Docker-демон (dockerd) — фоновый процесс в операционной системе, который обрабатывает запросы Docker API и управляет объектами Docker: образами, контейнерами, сетями и томами.
Docker Client
Docker-клиент — инструмент командной строки (Comand Line Interface — CLI), через который пользователь взаимодействует с демоном.
Когда вы используете команду docker run
, то Docker-клиент отправляет команду dockerd. Аналогичная история с другими командами docker <команда>
.
Docker Hub
Docker Hub — это общедоступный Docker registry, то есть хранилище всех доступных Docker-образов. При необходимости можно разворачивать свои приватные Docker registry, размещать собственные реестры Docker и использовать их для извлечения образов.
Запуск и начальная настройка Docker
Для работы потребуются:
- базовые навыки работы с командной строкой;
- Git;
- Docker.
Docker — довольно популярный инструмент, и установить его на любую ОС не составит труда. В руководстве «Начало работы с Docker» есть подробные инструкции по настройке Docker на Mac, Linux и Windows.
После установки Docker стоит проверить, что он работает.
Для этого выполните:
$ docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
2db29710123e: Pull complete
Digest: sha256:62af9efd515a25f84961b70f973a798d2eca956b1b2b026d0a4a63a3b0b6a3f2
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly
**....**
Запускаем Busybox
Теперь, когда Docker установлен, запустим в нём первый контейнер. За основу контейнера возьмите Busybox image. Введите в терминале команду:
$ docker pull busybox
ПримечаниеВы можете увидеть ошибку permission denied
после выполнения команды. Если вы работаете на Mac, убедитесь, что ядро Docker (engine) запущено. Если вы работаете в Linux, добавьте к командам docker
префикс sudo
. Кроме того, вы можете создать docker group, чтобы избавиться от этой проблемы.
Команда pull
скачает (спулит) busybox image из Docker registry и сохранит его в вашей системе.
Чтобы увидеть список всех образов в вашей системе, используйте команду docker images
:
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
busybox latest ff4a8eb070e1 2 days ago 1.24MB=
Docker Run
Отлично! Давайте теперь запустим Docker-контейнер на основе этого образа. Используйте команду docker run
:
$ docker run busybox
$
Пусть вас не смущает, что ничего не произошло. Ошибки здесь нет, и всё идёт по плану. Когда вы вызываете run
, Docker-клиент находит образ (в нашем случае busybox), загружает контейнер и запускает в нём команду.
Когда вы запустили docker run busybox
, то не передали команду, поэтому контейнер загрузился, выполнил ничего и затем вышел.
Давайте передадим команду и посмотрим, что будет:
$ docker run busybox echo "hello from busybox"
hello from busybox
Ура, хоть какой-то результат! Docker клиент выполнил команду echo
в busybox-контейнере, а затем вышел из него. И всё это произошло довольно быстро.
Хорошо, контейнер вы запустили, а как посмотреть, какие контейнеры запущены на сервере прямо сейчас? Для этого есть команда docker ps
:
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
Сейчас нет запущенных контейнеров, и вы видите пустую строку. Попробуйте более полезный вариант — docker ps -a
:
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c3368945dc3a busybox "echo 'hello from bu…" 7 minutes ago Exited (0) 7 minutes ago zealous_hugle
Появился список всех контейнеров, которые вы запускали. Заметьте, столбец STATUS
показывает, что эти контейнеры были закрыты несколько минут назад.
Итак, вы запустили контейнер, выполнили одну команду, и контейнер завершился. Какой в этом смысл? Может быть, есть способ запускать больше одной команды?
Конечно, есть! Давайте выполним docker run -it busybox sh
:
$ docker run -it busybox sh
/ # ls
bin dev etc home proc root sys tmp usr var
/ # uptime
22:34:42 up 35 min, 0 users, load average: 0.02, 0.01, 0.00
/ #
run
с флагами -it
подключит вас к интерактивному терминалу в контейнере. Теперь можно запускать в контейнере столько команд, сколько захотите.
Попробуйте выполнить ваши любимые команды в контейнере. А ещё стоит потратить немного времени на изучение возможностей команды run
, так как именно её вы будете использовать чаще всего.
Чтобы увидеть список всех флагов, которые поддерживает run
, выполните docker run --help
.
Docker rm
Раз вы научились создавать контейнеры, нужно потренироваться их удалять. Вы сами видели, что даже после остановки контейнера информация о нём остаётся на хосте. Можно запускать docker run
несколько раз и получать бесхозные контейнеры, которые будут занимать место на диске.
Место на диске нерезиновое, поэтому надо прибираться и удалять ненужные контейнеры. В этом поможет команда docker rm
:
# какие есть контейнеры
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f3776be78165 busybox "sh" 17 minutes ago Exited (130) 3 seconds ago optimistic_elion
c3368945dc3a busybox "echo 'hello from bu…" 33 minutes ago Exited (0) 33 minutes ago zealous_hugle
# удаление контейнеров по CONTAINER ID
$ docker rm f3776be78165 c3368945dc3a
# проверка, что контейнеры удалились
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
Если на хосте много контейнеров, которые надо удалить, то придётся копировать много CONTAINER ID
, а это может быть утомительно. Чтобы облегчить себе жизнь, можно использовать docker container prune
:
$ docker container prune
WARNING! This will remove all stopped containers.
Are you sure you want to continue? [y/N] y
Deleted Containers:
f3776be78165721b1e7e00234a29eb962ee7d678821f7632b538540cfa537701
Команда удалит все остановленные контейнеры.
Чтобы удалить образы, которые больше не нужны, запустите docker image prune
.
Развёртывание веб-приложения
Static-site
Итак, вы рассмотрели запуск docker
и поиграли с контейнером. Настало время перейти к более реальным вещам и развернуть веб-приложение с помощью Docker.
Первым делом запустите очень простой статический сайт. Для этого заберите Docker-образ из Docker Hub, запустите его и проверьте, что у вас есть рабочий веб-сервер.
Образ, который вы будете использовать, — одностраничный веб-сайт, специально созданный для демонстрации и размещённый в registry — ifireice/static-site
.
Вы можете загрузить и запустить образ сразу, используя docker run
, флаг --rm
автоматически удалит контейнер при выходе из него, а флаг -it
запустит интерактивный терминал, из которого можно выйти с помощью Ctrl+C. Контейнер при этом будет уничтожен.
$ docker pull ifireice/static-sitedocker run --rm -it ifireice/static-site
Так как образа ещё нет на хосте, Docker-клиент сначала скачает образ с registry, а потом запустит его. Если всё пойдёт по сценарию, вы должны увидеть сообщение Nginx is running...
в терминале.
Сервер запущен, но как увидеть сайт? На каком порту работает сайт? Как получить доступ к контейнеру?
Клиент не предоставляет никаких портов, поэтому вам нужно повторно запустить docker run
и опубликовать порты. Нажмите Ctrl+C, чтобы остановить контейнер.
Также вам надо сделать так, чтобы работающий контейнер не был привязан к терминалу. Это нужно для того, чтобы после закрытия терминала контейнер продолжил работать, — принцип действия detached mode:
$ docker run -d -P --name static-site ifireice/static-site
9c9e7a8a552795c0312bcdf3cb8949ddeea7bdd60bbf683140b99abf3b43bff1
-d
— отсоединить терминал,-P
— опубликовать все открытые порты на случайные порты,--name
— задать имя контейнеру.
Теперь вы можете увидеть порты, запустив команду docker port [CONTAINER]
:
$ docker port static-site
80/tcp -> 0.0.0.0:55000
Откройте http://localhost:55000 в браузере. Также можно указать собственный порт, на который Docker-клиент будет перенаправлять подключения к контейнеру.
$ docker run -p 8888:80 ifireice/static-site
Nginx is running...
Если вы устали писать «Hello world!», самое время перейти на «Hello Docker!»
Чтобы остановить контейнер, запустите docker stop
, указав идентификатор контейнера. В этом случае можно использовать имя static-site
, которое вы задали контейнеру при запуске.
$ docker stop static-site
static-site
Чтобы развернуть этот же сайт на удалённом сервере, вам нужно установить Docker и запустить указанную выше команду.
Создание Docker Image
Теперь, когда вы посмотрели, как запустить веб-сервер внутри образа Docker, наверное, хочется создать собственный Docker-образ?
Помните команду docker images
, которая выводит список образов, располагающихся локально?
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ifireice/django-app latest 07f6ba4cc25b 23 seconds ago 945MB
ifireice/static-site latest b237e7cf6bd2 27 minutes ago 142MB
ubuntu 18.04 71cb16d32be4 2 days ago 63.1MB
busybox latest ff4a8eb070e1 2 days ago 1.24MB
Перед вами список образов, скачанных из registry, а также образы, которые созданы нами:
TAG
— относится к конкретному снимку изображения;IMAGE ID
— уникальный идентификатор этого image.
Образы могут быть зафиксированы с изменениями и иметь несколько версий. Если вы не укажете конкретный номер версии, по умолчанию для клиента будет установлена последняя — latest
. Например, вы можете вытащить конкретную версию образа ubuntu
:
$ docker pull ubuntu:18.04
Новый образ можно или скачать из registry, или создать собственный.
Первый image
Допустим, вы хотите создать образ, который засунет в контейнер простое приложение на Django, отображающее случайную картинку с котиком. Для начала клонируйте это приложение к себе на локальный компьютер (не в Docker-контейнер):
$ git clone https://github.com/ifireice/docker-article.git
$ cd docker-article
Теперь это приложение нужно упаковать в image. Здесь пригодятся определения про образы.
- Базовые образы — это образы, у которых нет родительского образа. Обычно это образы ОС — ubuntu, busybox или debian;
- Дочерние образы — это образы, созданные на основе базовых образов с дополнительной функциональностью.
Также есть такие понятия, как официальный и пользовательский образы.
- Официальные образы поддерживаются Docker-сообществом. Обычно их имя состоит из одного слова, например, python, ubuntu, busybox и hello-world.
- Пользовательские образы созданы пользователями. Они строятся на основе базового и содержат дополнительную функциональность. Только поддерживаются уже не сообществом, а пользователем, который его создал. Имя у таких образов обычно имеет вид имя пользователя/изображения.
Вы будете создавать пользовательский образ, основанный на Python, потому что используете приложение на Django. Также вам понадобится Dockerfile.
Dockerfile
Dockerfile — это простой текстовый файл со списком команд, которые Docker-клиент вызывает при создании образа. Команды почти как в Linux, а значит, не нужно изучать ещё один язык для создания Dockerfile.
В директории приложения уже есть Dockerfile
, но вы будете создавать его с нуля. Поэтому переименуйте его и создайте пустой файл с именем Dockerfile
в директории Django-приложения.
Начните с определения базового image. Для этого используйте ключевое слово FROM
:
FROM python:3.8
Потом задайте рабочую директорию и скопируйте все файлы приложения:
# установить каталог для приложения
WORKDIR /usr/src/app
# копировать все файлы в контейнер
COPY . .
Теперь, когда у вас есть файлы, можете установить зависимости:
# установка зависимостей
RUN pip install --no-cache-dir -r requirements.txt
Добавьте порт, который нужно открыть. Приложение работает на порту 5000, его и укажите:
EXPOSE 5000
Последний шаг — написать очень простую команду для запуска приложения: python ./manage.py runserver 0.0.0.0:5000
. Для этого используйте команду CMD
. Она говорит, какую команду должен запустить контейнер при старте.
CMD ["python", "./manage.py", "runserver", "0.0.0.0:5000"]
Теперь ваш Dockerfile
готов и выглядит вот так:
FROM python:3.8
# установить каталог для приложения
WORKDIR /usr/src/app
# копировать все файлы в контейнер
COPY . .
# установка зависимостей
RUN pip install --no-cache-dir -r requirements.txt
# какой порт должен экспоузить контейнер
EXPOSE 5000
# запуск команды
CMD ["python", "./manage.py", "runserver", "0.0.0.0:5000"]
Раз у вас есть Dockerfile
, нужно собрать образ. Для этого используйте docker build
и передайте необязательный флаг -t
— имя тега и расположение каталога, содержащего Dockerfile
.
Чтобы сохранить (запушить) готовый image на Docker Hub, нужно создать там учётную запись. Сохранитесь, чтобы потом вы могли получить образ и развернуть контейнер на его основе на любом сервере.
В теге yourusername
должно быть имя вашей учетной записи в Docker Hub, иначе ничего не сработает.
$ docker build -t yourusername/cats .
[+] Building 8.4s (10/10) FINISHED
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 354B 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B
....
=> => writing image sha256:398306665e853e7c8cbe1f6456951ca63e782bb286dff5dc9361b191260e0ce3 0.0s
=> => naming to docker.io/yourusername/cats
Если на локальной машине нет образа python:3.8
, Docker-клиент сначала скачает образ, а затем создаст ваш. Тогда вывод команды может отличаться.
Если всё прошло хорошо, то image готов! Запустите его, не забыв изменить yourusername
на правильный:
$ docker run -p 8888:5000 yourusername/cats
Команда взяла порт 5000
внутри контейнера и сопоставила его с портом 8888
на хосте. И теперь, если вы обратитесь на порт 8888
хостовой машины, запрос будет перенаправлен в контейнер на порт 5000
. Узнать, что вернёт приложение на запрос, можно с помощью пути: http://0.0.0.0:8888
.
Котик дня
Поздравляем! Вы успешно создали свой первый Docker-образ.
Docker push
Осталось дело за малым — сохранить ваш образ в registry. Сначала авторизуйтесь в Docker Hub? Не забудьте про логин из yourusername
.
$ docker login --username yourusername 1 1772 04:12:45
Password:
Login Succeeded
Logging in with your password grants your terminal complete access to your account.
For better security, log in with a limited-privilege personal access token. Learn more at https://docs.docker.com/go/access-tokens/
Когда будете вводить пароль, он не отобразится в консоли. Это норма, он и не должен быть виден всем подряд.
Чтобы сохранить образ в registry, просто введите docker push yourusername/cats
. Важно, чтобы тег имел формат yourusername/image_name
. Тогда Docker-клиент будет знать, куда сохранять образ:
$ docker push yourusername/cats
Как только образ сохранится в registry, его можно увидеть в Docker Hub по адресу https://hub.docker.com/r/yourusername/cat.
Ну а забирать и запускать image с registry вы уже умеете!
Выводы
Главные мысли этой работы:
- разобрались с виртуальными машинами, контейнерами и поняли, чем они отличаются;
- узнали, что такое Docker, и обсудили основную терминологию: image, container, docker daemon, docker client, registry;
- запустили Hello Docker;
- запустили контейнер на базе образа из Docker Hub;
- создали свой контейнер и сохранили на Docker Hub.
Дополнительные материалы по Docker
Если хочется изучить Docker глубже, отправляйтесь по ссылкам:
- DevOps для эксплуатации и разработки
- Начало работы с Docker
- Dockerfile
- Docker registry
- Docker volumes
- Docker Network
- Docker compose
- Image-building best practices
- Видеоурок по Docker для начинающих
- 12 факторов
Contents
- 1 Docker и Docker-Compose — Tutorial и подборка видео по темам
- 1.1 Что такое Docker и зачем он нужен?
- 1.1.1 Видео — Что такое Docker за 200 секунд
- 1.1.2 Сущности Docker: docker daemon, container, image, Dockerfile, Docker Registry
- 1.2 Что такое docker image (образ)
- 1.3 Что такое docker container (контейнер)
- 1.4 Управление контейнерами. Схема Lifecycle of Docker Container
- 1.5 Что такое Docker Hub?
- 1.6 Как создать свой образ? Что такое Dockerfile?
- 1.6.1 Пример dockerfile
- 1.6.2 Команды Dockerfile
- 1.6.3 Пример dockerfile для приложения flask app python
- 1.6.4 Видео Tutorials — Dockerfile
- 1.7 Что такое Docker Volume?
- 1.7.1 Команды Docker Volume
- 1.7.2 Подборка видео по Docker Volume
- 1.8 Как взаимодействовать с контейнером?
- 1.9 Docker Networking
- 1.10 Что такое Docker Compose?
- 1.11 docker-compose.xml
- 1.12 Дополнительные подборки видео по Docker
- 1.1 Что такое Docker и зачем он нужен?
- 2 Примеры создания приложений с помощью Docker или Docker-Compose
-
- 2.0.1 Docker + ReactJS tutorial: Development to Production workflow + multi-stage builds + docker compose
-
- 3 Краткий экскурс в Linux
- 3.1 Базовый список команд Linux
- 3.2 Linux File System/Structure Explained
- 3.3 Основы Ubuntu Linux: apt-get, bash, командная строка
- 3.4 Linux command line for beginners
- 3.5 Права Доступа и владения файлами и директориями
- 4 Установка Докера на Linux. Install Docker on Ubuntu 20.04
- 5 Portainer — что это такое?
- 5.1 Установка Portainer внутри Docker
- 6 GitHub Actions — CI/CD Pipeline with Docker
- 7 Использованные источники для подготовки статьи и другие полезные статьи
Что такое Docker и зачем он нужен?
Официальный сайт docker.com.
Docker — это платформа для разработки, развертывания и запуска приложений внутри контейнеров.
Docker – это технология с открытым исходным кодом, которая решает проблемы развертывания и масштабирования путем отделения приложений от зависимостей инфраструктуры. Она решает эти проблемы благодаря применению контейнеров, позволяющих упаковать приложение со всеми его зависимостями, включая структуру каталогов, метаданные, пространство процессов, номера сетевых портов и т. д. Приложение, упакованное в контейнер, запускается одинаково на любых машинах и в любых окружениях. Именно эта особенность сделала технологию Docker особенно интересной и обеспечила ей стремительный взлет.
И еще одно определение докера:
Docker — это инструмент с открытым исходным кодом, который позволяет вам включать и хранить ваш код и его зависимости в удобном пакете, который называется образом. Затем этот образ можно использовать для создания экземпляра вашего приложения (сервиса) — контейнера. Основное различие между контейнерами и виртуальными машинами заключается в том, что контейнеры охватывают только уровень приложения и полагаются на базовое ядро операционной системы, в случае виртуальной машины создается новый экземпляр операционной системы.
Видео — Что такое Docker за 200 секунд
Сущности Docker: docker daemon, container, image, Dockerfile, Docker Registry
В Docker используется архитектура клиент/сервер, в соответствии с которой клиент взаимодействует с демоном Docker, а тот предоставляет все необходимые клиенту услуги.
Рассмотрим компоненты рабочего процесса и инструменты для управления контейнерами и их развертывания, составляющие экосистему Docker:
- Daemon Docker (Сервер): Выполняется в хост-системе и управляет всеми запущенными контейнерами. Docker Daemon — это сервер Docker, который прослушивает запросы Docker API. Docker Daemon управляет образами, контейнерами, сетями и томами.
- Docker Container (Контейнер): Автономная виртуальная система, содержащая выполняющийся процесс, все файлы, зависимости, адресное пространство процесса и сетевые порты, необходимые приложению. Так как каждый контейнер имеет свое пространство портов, следует организовать их отображение в фактические порты на уровне Docker;
- Docker Client (Клиент): Пользовательский интерфейс, или интерфейс командной строки, для взаимодействий с демоном Docker. Клиент Docker — это основной способ взаимодействия с Docker. Когда вы используете интерфейс командной строки (CLI) Docker, вы вводите в терминал команду, которая начинается с
docker
. Затем клиент Docker использует API Docker для отправки команды демону Docker. - Docker Image (Образ): это шаблон только для чтения, который содержит набор инструкций по созданию контейнера, который может работать на платформе Docker. Он предоставляет удобный способ упаковать приложения и предварительно настроенные серверные среды, которые вы можете использовать для личного использования или публично публиковать с другими пользователями Docker. Также можно воспользоваться командой docker diff, чтобы увидеть различия между двумя образами. Каждый образ состоит из нескольких уровней, или слоев, которые могут совместно использоваться несколькими образами.
- Реестр Docker: Репозиторий для хранения и распространения образов контейнеров Docker. Пример известного реестра — Docker Hub, куда можно помещать и откуда можно извлекать образы.
- Dockerfile: Это очень простой текстовый файл, содержащий команды, которые выполняют сборку образов Docker. Посредством этих команд можно устанавливать дополнительные программные компоненты, настраивать переменные окружения, рабочие каталоги и точку входа ENTRYPOINT, а также добавлять новый код;
- Docker Swarm: По сути своей, это готовый к использованию механизм кластеризации, позволяющий объединить несколько узлов Docker в один большой хост Docker.
- Docker Compose: Приложения часто состоят из множества компонентов, и соответственно они будут выполняться в нескольких контейнерах. В состав Docker входит инструмент Compose, с помощью которого можно легко запустить приложение в нескольких контейнерах. Вы можете определить окружение для приложения в общем файле Dockerfile и определить перечень служб в файле docker-compose.yml, после чего Docker автоматически будет создавать и запускать необходимые контейнеры, как определено в этих файлах.
Что такое docker image (образ)
Образ Docker — это шаблон только для чтения, который содержит набор инструкций по созданию контейнера, который может работать на платформе Docker. Образ — это главный шаблон, который используется для запуска одинаковых контейнеров. Если выразиться кратко, то docker image — это переносимый формат для одного контейнера.
Образ Docker состоит из набора файлов, которые объединяют воедино все необходимое, например installations, application code, и dependencies, необходимые для настройки полностью работоспособной среды контейнера. Вы можете создать образ Docker одним из двух способов:
- Интерактивный: запустив контейнер из существующего образа Docker, вручную изменив среду контейнера с помощью серии активных шагов и сохранив полученное состояние как новый образ.
- Dockerfile: путем создания текстового файла, известного как Dockerfile, который предоставляет спецификации для создания образа Docker.
Dockerfile — это файл с инструкциями о том, как Docker должны строить свой image.
С физической точки зрения docker image состоит из набора слоев, доступных только для чтения (read-only layers). Слои image работают следующим образом:
- Каждый слой image является результатом одной команды в файле Dockerfile. Образ докера представляет собой сжатый (tar) файл, содержащий серию слоев.
- Каждый дополнительный слой image включает только набор отличий от предыдущего слоя (попробуйте запустить для docker image команду docker history, которая выведет все его слои и те команды, которые их создало).
Пример Dockerfile:
FROM node:13.12.0—alpine WORKDIR /usr/src/app COPY package*.json ./ RUN npm install COPY . . |
Что такое docker container (контейнер)
Docker Container — это исполняемый экземпляр образа. Образ Docker плюс команда docker run image_name
создает и запускает контейнер из образа.
Контейнеры предлагают виртуальную среду, в которую упаковываются процесс приложения, метаданные и файловая система, т.е. все, что необходимо приложению для работы. В отличие от виртуальных машин, контейнеры не требуют собственной операционной системы — это всего лишь обертки вокруг процессов UNIX, которые непосредственно взаимодействуют с ядром.
Контейнеры обеспечивают полную изоляцию приложений и процессов, когда одно приложение ничего не знает о существовании других приложений. Но все процессы используют одно и то же ядро операционной системы.
Управление контейнерами. Схема Lifecycle of Docker Container
Образ Docker — это статическая модель, содержащая предустановленное приложение. При запуске этого образа создается контейнер . Образ можно использовать для запуска любого количества контейнеров. На следующей диаграмме показан жизненный цикл контейнера Docker и связанных команд.
Пример схемы docker container lifecycle 1:
Пример схемы docker container lifecycle 2:
Create container
Создайте контейнер, чтобы в дальнейшем запустить его с нужным образом.
docker create —name <container—name> <image—name>
Run docker container
Запустите контейнер докера с требуемым образом и указанной командой / процессом. Флаг -d используется для запуска контейнера в фоновом режиме.
docker run —it —d —name <container—name> <image—name> bash
Pause container
Используется для приостановки процессов, запущенных внутри контейнера.
docker pause <container—id/name>
Unpause container
Используется для возобновления процессов внутри контейнера.
docker unpause <container—id/name>
Start container
Запустите контейнер, если он находится в остановленном состоянии.
docker start <container—id/name>
Stop container
Остановить контейнер и процессы, запущенные внутри контейнера:
docker stop <container—id/name>
Чтобы остановить все запущенные контейнеры докеров
docker stop $(docker ps —a —q)
Restart container
Используется для перезапуска контейнера, а также процессов, работающих внутри контейнера.
docker restart <container—id/name>
Kill container
Мы можем убить работающий контейнер.
docker kill <container—id/name>
Destroy container
Лучше уничтожать контейнер, только если он находится в остановленном состоянии, вместо того, чтобы принудительно уничтожать работающий контейнер.
docker rm <container—id/name>
Чтобы удалить все остановленные контейнеры докеров
docker rm $ (docker ps —q —f status = exited)
Что такое Docker Hub?
Docker Hub — это облачная служба реестра, которая позволяет загружать образы Docker, созданные другими сообществами. Вы также можете загрузить свои собственные образы, созданные Docker, в Docker Hub.
Как создать свой образ? Что такое Dockerfile?
Что такое Dockerfile?
- Dockerfile — это текстовый файл конфигурации, написанный с использованием специального синтаксиса.
- В нем описываются пошаговые инструкции по всем командам, которые необходимо выполнить для сборки образа Docker.
- Команда
docker build
обрабатывает этот файл, создавая образ Docker в вашем локальном кэше образов, который затем можно запустить с помощьюdocker run
команды или отправить в постоянный репозиторий образов (push).
Как создать Dockerfile?
Dockerfile создается в любом текстовом редакторе. Далее пишутся инструкции.
Пример dockerfile
Пример dockerfile для NGINX:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
# Каждая инструкция в этом файле генерирует новый слой, который помещается в ваш локальный кеш docker image # Строки, которым предшествует #, считаются комментариями и игнорируются # В строке ниже указано, что мы будем основывать наш новый образ на последней официальной версии Ubuntu FROM ubuntu: latest # Указываем, кто поддерживает docker image (автор) LABEL Maintainer = «myname@somecompany.com» # Обновляем образ до последних пакетов RUN apt—get update && apt—get upgrade —y # Устанавливаем NGINX RUN apt—get install nginx —y # Выставить порт 80 EXPOSE 80 # Последний пункт — это команда для запуска NGINX в нашем контейнере CMD [«nginx», «-g», «daemon off;»] |
Команды Dockerfile
ADD — определяет файлы для копирования из файловой системы хоста в контейнер
ADD ./local/config.file /etc/service/config.file |
CMD — это команда, которая будет запускаться при запуске контейнера
CMD [“nginx”, “—g”, “daemon off;”] |
ENTRYPOINT — устанавливает приложение по умолчанию, используемое каждый раз, когда контейнер создается из образа. Если используется вместе с CMD, вы можете удалить приложение и просто определить там аргументы
CMD Hello World! ENTRYPOINT echo |
ENV — установка / изменение переменных среды в контейнерах, созданных из образа
EXPOSE — Определите, какие порты контейнера открывать
FROM — Выберите базовый образ, чтобы построить новый образ поверх
LABEL Maintainer — необязательное поле, чтобы вы могли идентифицировать себя как maintainer этого образа. Это просто ярлык
LABEL maintainer=someone@xyz.xyz” |
RUN — укажите команды для внесения изменений в ваш образ, а затем контейнеры, запускаемые с этого образа. Это включает в себя обновление пакетов, установку программного обеспечения, добавление пользователей, создание исходной базы данных, настройку сертификатов и т. д. Это команды, которые вы должны запускаться из командной строки для установки и настройки вашего приложения. Это одна из самых важных директив dockerfile
RUN apt—get update && apt—get upgrade —y && apt—get install —y nginx && rm —rf/var/lib/apt/lists/* |
USER — Определите пользователя по умолчанию, все команды будут запускаться, как в любом контейнере, созданном из вашего образа. Это может быть либо UID, либо имя пользователя.
VOLUME — создает точку монтирования в контейнере, связывая ее с файловыми системами, доступными хосту Docker. Новые тома заполняются уже существующим содержимым указанного места на image. Особенно уместно упомянуть, что определение томов в Dockerfile может привести к проблемам. Томами следует управлять с помощью команд docker-compose или docker run. Volumes не являются обязательными. Если у вашего приложения нет состояния (и большинство веб-приложений работают так), вам не нужно использовать тома. Volume требуются для баз данных, например, или для сохранения логов приложения.
WORKDIR — Определите рабочий каталог по умолчанию для команды, определенной в инструкциях «ENTRYPOINT» или «CMD».
Пример dockerfile для приложения flask app python
FROM python:3.6.4—alpine3.6 ENV FLASK_APP=minitwit COPY . /app WORKDIR /app RUN pip install —editable . RUN flask initdb EXPOSE 5000 CMD [ «flask», «run», «—host=0.0.0.0» ] |
Видео Tutorials — Dockerfile
Video: Dockerfile Tutorial — Docker in Practice
Видео: Docker создаем собственный образ
Видео: Dockerfile. Формат и создание образа контейнера
Что такое Docker Volume?
Контейнеры Docker используются для запуска приложений в изолированной среде. По умолчанию все изменения внутри контейнера теряются при остановке контейнера. Если мы хотим сохранить данные между запусками, могут помочь Docker Volume.
Когда мы запускаем новый контейнер, Docker добавляет слой чтения-записи поверх слоев image, позволяя контейнеру работать как в стандартной файловой системе Linux .
Таким образом, любое изменение файла внутри контейнера создает рабочую копию на уровне чтения-записи. Однако, когда контейнер останавливается или удаляется, этот уровень чтения-записи теряется.
При монтировании привязки (bind mount) используется файловая система хоста, но Docker Volumes встроены в Docker. Данные хранятся где-то в хранилище, подключенном к хосту — часто в локальной файловой системе:
Volumes
— это предпочтительный механизм сохранения данных, генерируемых и используемых контейнерами Docker. Volumes
часто является лучшим выбором, чем сохранение данных в доступном для записи слое контейнера, поскольку том не увеличивает размер контейнеров, использующих его, и содержимое тома существует вне жизненного цикла данного контейнера.
В docker-compose.yml
, volumes
может появляться в двух разных местах:
version: «3.7» services: database: # … volumes: # Вложенный ключ. Настраивает тома для определенной службы. volumes: # Ключ верхнего уровня. Объявляет тома, на которые можно ссылаться из нескольких сервисов. # … |
Команды Docker Volume
Command | Description |
docker volume create |
Create a volume |
docker volume inspect |
Display detailed information on one or more volumes |
docker volume ls |
List volumes |
docker volume prune |
Remove all unused local volumes |
docker volume rm |
Remove one or more volumes |
Подборка видео по Docker Volume
Docker Volumes explained in 6 minutes
Docker Volumes (Тома) урок 8
Attaching volumes to containers
Как взаимодействовать с контейнером?
Docker не открывает порты по умолчанию, вы должны настроить каждый открытый порт самостоятельно!
Чтобы сопоставить порт на хосте с контейнером, нам нужно использовать флаг -p команды docker run:
docker run —p <port_number_on_host>:<port_number_on_container> <image> |
или
docker run —v <порт_на_хосте>:<порт_в_контейнере> <образ> |
Следующая команда запустит контейнер для mlflow и сопоставит порт 7000 этого контейнера с портом 7000 хоста докера:
docker run —p 7000:7000 mlflow |
Аналогичным образом будет работать контейнер для POSTGRESQL и порта 5432 этого контейнера в порт 5432 от DOCKER хоста:
docker run —p 5432:5432 postgresql |
Из-за того, что многие контейнеры связаны с одним хостом, когда служба уже запущена на порту на хосте, мы не можем запустить другой контейнер с этим же портом. Следовательно, чтобы запустить другой экземпляр postgresql на хосте, мы должны выбрать порт, который не используется.
docker run —p 1234:5432 postgresql |
Открытие всех портов Docker, как правило, не является хорошей идеей, поскольку по умолчанию все порты закрыты.
Чтобы открыть только один порт, выполните следующую строку:
docker container run —p 8080:80 —d nginx |
Порт 80 контейнера Nginx доступен для внешнего мира через порт хоста 8080.
По умолчанию Docker предоставляет порты контейнера IP-адресу 0.0.0.0 (это соответствует любому IP-адресу в системе). Вы также можете указать Docker, какой IP-адрес нужно привязать. Это может быть 127.0.0.1 или другой IP-адрес.
Чтобы привязать порт 80 контейнера Docker к порту 8000 хост-системы и IP-адресу 127.0.0.1 (он же localhost), просто выполните следующую команду:
docker run —d —p 127.0.0.1:8000:80 nginx |
Docker Networking
Docker Networking позволяет соединять контейнеры Docker вместе. Подключенные контейнеры Docker могут находиться на одном или нескольких хостах.
Docker Networking
Docker Networking Tutorial
Что такое Docker Compose?
Docker Compose — это инструмент, который упрощает запуск приложений, состоящих из нескольких контейнеров.
Docker Compose позволяет записывать команды в docker-compose.yml
файл для повторного использования. Интерфейс командной строки Docker Compose (cli) упрощает взаимодействие с вашим многоконтейнерным приложением. Docker Compose поставляется бесплатно с установленным вами Docker.
Схема работы Docker-Compose:
Docker-Compose in 12 Minutes
docker-compose.xml
docker-compose.yml
— это файл Docker-Compose, который содежит инструкции, используемые для запуска и настройки сервисов (отдельных контейнеров нашего многоконтейнерного приложения).
Структура файла docker-compose.xml (источник — Руководство по Docker Compose для начинающих):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
# Файл docker-compose должен начинаться с тега версии. # Мы используем «3» так как это — самая свежая версия на момент написания этого кода. version: «3» # Следует учитывать, что docker-composes работает с сервисами. # 1 сервис = 1 контейнер. # Сервисом может быть клиент, сервер, сервер баз данных… # Раздел, в котором будут описаны сервисы, начинается с ‘services’. services: # Как уже было сказано, мы собираемся создать клиентское и серверное приложения. # Это означает, что нам нужно два сервиса. # Первый сервис (контейнер): сервер. # Назвать его можно так, как нужно разработчику. # Понятное название сервиса помогает определить его роль. # Здесь мы, для именования соответствующего сервиса, используем ключевое слово ‘server’. server: # Ключевое слово «build» позволяет задать # путь к файлу Dockerfile, который нужно использовать для создания образа, # который позволит запустить сервис. # Здесь ‘server/’ соответствует пути к папке сервера, # которая содержит соответствующий Dockerfile. build: server/ # Команда, которую нужно запустить после создания образа. # Следующая команда означает запуск «python ./server.py». command: python ./server.py # Вспомните о том, что в качестве порта в ‘server/server.py’ указан порт 1234. # Если мы хотим обратиться к серверу с нашего компьютера (находясь за пределами контейнера), # мы должны организовать перенаправление этого порта на порт компьютера. # Сделать это нам поможет ключевое слово ‘ports’. # При его использовании применяется следующая конструкция: [порт компьютера]:[порт контейнера] # В нашем случае нужно использовать порт компьютера 1234 и организовать его связь с портом # 1234 контейнера (так как именно на этот порт сервер # ожидает поступления запросов). ports: — 1234:1234 # Второй сервис (контейнер): клиент. # Этот сервис назван ‘client’. client: # Здесь ‘client/ соответствует пути к папке, которая содержит # файл Dockerfile для клиентской части системы. build: client/ # Команда, которую нужно запустить после создания образа. # Следующая команда означает запуск «python ./client.py». command: python ./client.py # Ключевое слово ‘network_mode’ используется для описания типа сети. # Тут мы указываем то, что контейнер может обращаться к ‘localhost’ компьютера. network_mode: host # Ключевое слово ‘depends_on’ позволяет указывать, должен ли сервис, # прежде чем запуститься, ждать, когда будут готовы к работе другие сервисы. # Нам нужно, чтобы сервис ‘client’ дождался бы готовности к работе сервиса ‘server’. depends_on: — server |
Пример сложного файла docker-compose.xml:
Если хотите реально разобраться в docker — очень рекомендую курс Docker и Docker Compose — Деплой проекта с нуля
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
version: ‘3’ services: frontend: build: dockerfile: Dockerfile.prod context: ./frontend container_name: docker—frontend restart: unless—stopped command: serve —s build —l 3000 networks: — docker—network api: build: ./api container_name: docker—api command: npm run start restart: unless—stopped environment: — PORT=3001 — MONGO_URL=mongodb://api_db:27017/api — AUTH_API_URL=http://auth:3002/api depends_on: — api_db networks: — docker—network auth: build: ./auth container_name: docker—auth command: npm run start restart: unless—stopped environment: — PORT=3002 — MONGO_URL=mongodb://auth_db:27017/auth — API_URL=http://api:3001/api depends_on: — auth_db networks: — docker—network api_db: image: mongo:latest container_name: docker—api—db volumes: — mongodb_api:/data/db networks: — docker—network auth_db: image: mongo:latest container_name: docker—auth—db volumes: — mongodb_auth:/data/db networks: — docker—network nginx: image: nginx:stable—alpine container_name: docker—nginx ports: — «80:80» volumes: — ./nginx/nginx.conf.prod:/etc/nginx/conf.d/nginx.conf depends_on: — frontend — api — auth networks: — docker—network volumes: mongodb_api: mongodb_auth: networks: docker—network: driver: bridge |
Дополнительные подборки видео по Docker
Что такое Docker? Вечерняя школа Слёрма по Kubernetes.
Основы Docker. Большой практический выпуск
Docker Tutorial for Beginners [FULL COURSE in 3 Hours]
Docker-compose что это?
Примеры создания приложений с помощью Docker или Docker-Compose
Docker + ReactJS tutorial: Development to Production workflow + multi-stage builds + docker compose
Краткий экскурс в Linux
Базовый список команд Linux
1. Команда help
Запомнить все возможные параметры, которые можно использовать для команды, невозможно. Если вы не использовали команду ранее или в течение длительного времени, у вас есть возможность изучить возможные варианты, которые можно передать. Большинство команд поддерживают передачу справки в качестве опции и отображают небольшое сообщение о том, как использовать команду.
<command_name> —help
Чтобы получить всю возможную помощь, есть команда man. Страница руководства (сокращенно man) — это документ, который иллюстрирует, что делает команда, возможные параметры, примеры использования и т. Д. Это вся помощь, которую вы можете получить для команды
man <command_name>
2. pwd: pwd печатает имя текущего рабочего каталога. Когда вы открываете терминал, вы попадаете в домашний каталог пользователя, под которым вы вошли в систему. pwdпечатает абсолютный путь. Он начинается с /корневого каталога файловой системы Linux.
3. ls: ls распечатывает все файлы, которые присутствуют в каталоге, в котором вы находитесь. Вы можете получить дополнительную информацию о файлах, а также увидеть скрытые файлы с помощью этой -al опции.
4. cd: cd означает смену каталога. Если вы хотите перейти в другой каталог, используйте эту команду. При использовании без аргументов cd приведет вас в ваш домашний каталог. Вы можете передать абсолютный или относительный путь команде cd. Также обратите внимание, что это . представляет текущий каталог и .. родительский каталог.
# takes you to your home directory (comment) cd # To navigate to the parent directory cd ../ # To change to previous working directory cd ~ |
5. mv: mv (расширяется при перемещении) используется для перемещения файлов / каталогов из location1 в location2. Его также можно использовать для переименования файла.
# Renaming file a.txt to b.txt mv a.txt b.txt # move file from directory test in your current directory to tmp mv test/test.txt /tmp/test.txt # move multiple files in your current directory to /tmp mv a.txt b.txt c.txt /tmp # move file from a dir A to dir B by specifying the absolute paths mv /var/log/test.log /tmp/test.log |
6. cp: скопируйте файлы из location1 в location2. Каталоги можно копировать с помощью -R опции.
# copy file from your home to directory to another location cp test.txt /tmp/bckup # copy contents of directories recursively cp —R test_dir /tmp/bckup |
7. rm: rm (расширяется как remove) используется для удаления файла или каталога. При удалении файла нет отмены. Так что будьте осторожны, когда хотите что-то удалить
# rm a file in a location rm /home/test_user/test.txt # rm a directory that is empty rm —r <location_of_dir> # rm a directory with contents in it rm —rf <location_of_dir> |
8. mkdir: mkdir (расширенный как каталог make) используется для создания нового каталога в определенном месте.
# create a directory mkdir test_dir # create intermediate directory (test_directory) when creating a directory (test_directory_child) mkdir —p /home/test_user/test_directory/test_directory_child |
9. rmdir: удалить каталог. Это альтернативная команда дляrm -rf
# delete a directory test_directory_child rmdir /home/test_user/test_directory/test_directory_child # Delete the intermediate directories in the path. It only works if # the intermediate directories don’t contain any other child # directories other than the one specified rmdir —p test_dir/test_dir_child/test_dir_child2 |
10. cat: распечатывает содержимое файлов на терминале и возвращает обратно в командную строку. Существуют различные редакторы, которые вы можете использовать для просмотра содержимого файла (например, vim, nano и т. Д.), Но эта команда выводит содержимое в STDOUT (стандартный вывод).
# Print contents of a file. cat test.sh |
11. touch: Изменить file timestamps. Обновляет отметки времени в существующих файлах или создает файлы с текущей отметкой времени, если она не существует.
# Assuming file1.txt doesn’t exist, it creates a new file touch file1.txt |
12. sudo: Сокращение от SuperUser Do , эта команда помогает вам выполнять задачи, требующие административных разрешений. Однако не рекомендуется использовать эту команду случайным образом без причины, потому что любая ошибка, сделанная пользователем root, необратима.
# Read the contents of syslog sudo cat /var/log/syslog |
13. find: эта команда ищет в иерархии папок файл или каталог, соответствующий указанному имени или шаблону. Он выполняет рекурсивный поиск во всех каталогах вниз по дереву.
# find a file with name test.txt in a folder find /home/test_user —name test.txt # find file of specific pattern recursively find . —type f —name «*.sh» —R # find and remove multiple files following a same pattern find . —type f —name «*.py» —exec rm —f {} |
14.grep: эта команда печатает все строки в файле, соответствующие определенному шаблону.
# recursively grep for a pattern in a directory tree grep —r search_field /etc/ # search two different words egrep —w ‘x|y’ /home/test_user/* |
15.df: Эта команда сообщает об использовании дискового пространства файловой системы.
# lists all the file system, use -a option in human readable format i.e., power of 1024 df —ah |
16. du: оценка использования файлового пространства. Он имеет различные параметры для отображения вывода в желаемом формате.
# Display disk usage of all files and directories in the current directory du —ah # sum of sizes of files and directories in the directory specified du —sh /home/test_user |
17. uname: Распечатать системную информацию. Есть несколько вариантов, которые можно передать, если вам нужна конкретная информация о системе, такая как версия ядра, тип процессора, аппаратная платформа и т. Д.
# Prints all the information about the system uname —a |
18. lsblk: lsblk (Expanded as list block devices) используется для отображения всех блочных устройств в виде дерева. Он также предоставляет информацию о разделах, имеющихся на блочном устройстве.
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT sda 8:0 0 55.9G 0 disk └─sda1 8:2 0 55.9G 0 part / sdb 8:16 0 111.8G 0 disk └─sdb1 8:17 0 111.8G 0 part |
19. hostname: распечатать / установить имя хоста машины. Только суперпользователь может обновить имя хоста
# print hostname of system hostname # set hostname hostname set name <host_name> |
20. tail: отображает последнюю часть файлов. По умолчанию, если файл передан, он печатает последние 10 строк
# output appended data as the file grows; tail —f /var/log/syslog |
Linux File System/Structure Explained
Основы Ubuntu Linux: apt-get, bash, командная строка
Linux command line for beginners
Права Доступа и владения файлами и директориями
Установка Докера на Linux. Install Docker on Ubuntu 20.04
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
# Обновляем существующий список пакето sudo apt update # Далее устанавливаем пакеты, которые позволят apt использовать пакеты через HTTPS: sudo apt install apt—transport—https ca—certificates curl software—properties—common # Далее добавим ключ GPG для официального репозитория Docker curl —fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add — # Добавляем ремозиторий докер в источники apt sudo add—apt—repository «deb [arch=amd64] https://download.docker.com/linux/ubuntu focal stable» # Обновляем базу данных пакетов и добавим в нее пакеты Docker из недавно добавленного репозитория sudo apt update # Далее проверим, что установка будет выполняться из репозитория Docker, а не из репозитория Ubuntu по умолчанию apt—cache policy docker—ce # Мы должны получить следующий ответ (номер версии Docker может отличаться): # root@apache1superset:~# apt-cache policy docker-ce # docker-ce: # Installed: (none) # Candidate: 5:20.10.6~3-0~ubuntu-focal # Version table: # 5:20.10.6~3-0~ubuntu-focal 500 # 500 https://download.docker.com/linux/ubuntu focal/stable amd64 Packages # 5:20.10.5~3-0~ubuntu-focal 500 # 500 https://download.docker.com/linux/ubuntu focal/stable amd64 Packages # 5:20.10.4~3-0~ubuntu-focal 500 # 500 https://download.docker.com/linux/ubuntu focal/stable amd64 Packages # 5:20.10.3~3-0~ubuntu-focal 500 # … # Далее устанавливаем докер командой (на доп.вопрос отвечаем «yes») sudo apt install docker—ce # Docker будет автоматически установлен, также запустится демон-процесс и будет активирован запуск при загрузке. # Проверить статус докера можно командой (что он running/active): sudo systemctl status docker # Загружаем текущую стабильную версию Docker Compose sudo curl —L «https://github.com/docker/compose/releases/download/v2.5.0/docker-compose-$(uname -s)-$(uname -m)» —o /usr/local/bin/docker—compose # Применяем разрешения для исполняемого файла к двоичному файлу sudo chmod +x /usr/local/bin/docker—compose # Чтобы протестировать docker-compose (установилась версия или нет), запустим команду docker—compose —version |
Portainer — что это такое?
Portainer — это легкий пользовательский интерфейс управления, который позволяет легко управлять различными средами Docker и docker-containers. С помощью Portainer вы сможете в несколько кликов запустить на вашем сервере готовые контейнеры с популярным ПО и связать их между собой.
Описание с Github: Making Docker and Kubernetes management easy. На момент написания статьи 18.7k stars
Этот мощный набор инструментов с открытым исходным кодом, насчитывающий более полумиллиона постоянных пользователей, позволяет легко создавать и управлять контейнерами в Docker, Swarm, Kubernetes и Azure ACI.
По умолчанию мы управляем Docker через командную строку. Portainer предоставляет отличный и очень информативный пользовательский интерфейс для управления всеми аспектами Docker. Это диаграммы и информационные панели, которые предоставляют диаграммы использования ресурсов контейнера в реальном времени. Запуск и управление контейнерами и стеками можно выполнить несколькими щелчками мыши.
После установки portainer и захода внутрь вы видете первое окно настройки. У меня докер развернут локально, поэтому я подключаюсь через пункт local, нажимаю connect и попадаю в админку.
Домашняя страница админки выглядит следующим образом. Экземпляры Docker, о которых знает Portainer, отображаются в центральной части экрана.
Щелкните экземпляр Local Docker, чтобы увидеть панель мониторинга (Dashboard), на которой отображается общий обзор того, что в настоящее время выполняется в Docker.
Здесь нам открывается вся суть инструмента:
- Можно создать в несколько кликов из шаблонов приложения в докере
- Можно управлять сетью, контейнерами.
- Также можно создавать и настраивать Volumes:
Portainer — отличный инструмент, который можно использовать для управления контейнерами Docker, службами Swarm и другими ресурсами с помощью простого и интуитивно понятного пользовательского веб-интерфейса. Portainer предоставляет несколько бесплатных функций и платных плагинов, которые могут улучшить взаимодействие с пользователем при управлении кластерами Docker.
Установка Portainer внутри Docker
#portainer install sudo docker volume create portainer_data docker run —d —p 8000:8000 —p 9443:9443 —name portainer —restart=always —v /var/run/docker.sock:/var/run/docker.sock —v portainer_data:/data portainer/portainer—ce:2.9.3 |
Далее нужно перейти по ссылке https://68.133.234.42:9443/ (замените 68.133.234.42 на ваш адрес).
GitHub Actions — CI/CD Pipeline with Docker
Использованные источники для подготовки статьи и другие полезные статьи
- https://medium.com/@BeNitinAgarwal/lifecycle-of-docker-container-d2da9f85959
- Микросервисы и контейнеры Docker. Парминдер Сингх Кочер. Addison-Wesley. ДМК
- Learn Enough Docker to be Useful
- Building Docker Images with Dockerfiles
- [Docker] Основы Docker: Dockerfile и docker-compose.yml
-
20 Basic Linux Commands for Beginners!
Автор статьи, перевод которой мы сегодня публикуем, говорит, что она предназначена для тех разработчиков, которые хотят изучить Docker Compose и идут к тому, чтобы создать своё первое клиент-серверное приложение с использованием Docker. Предполагается, что читатель этого материала знаком с основами Docker. Если это не так — можете взглянуть на эту серию материалов, на эту публикацию, где основы Docker рассмотрены вместе с основами Kubernetes, и на эту статью для начинающих.
Что такое Docker Compose?
Docker Compose — это инструментальное средство, входящее в состав Docker. Оно предназначено для решения задач, связанных с развёртыванием проектов.
Изучая основы Docker, вы могли столкнуться с созданием простейших приложений, работающих автономно, не зависящих, например, от внешних источников данных или от неких сервисов. На практике же подобные приложения — редкость. Реальные проекты обычно включают в себя целый набор совместно работающих приложений.
Как узнать, нужно ли вам, при развёртывании некоего проекта, воспользоваться Docker Compose? На самом деле — очень просто. Если для обеспечения функционирования этого проекта используется несколько сервисов, то Docker Compose может вам пригодиться. Например, в ситуации, когда создают веб-сайт, которому, для выполнения аутентификации пользователей, нужно подключиться к базе данных. Подобный проект может состоять из двух сервисов — того, что обеспечивает работу сайта, и того, который отвечает за поддержку базы данных.
Технология Docker Compose, если описывать её упрощённо, позволяет, с помощью одной команды, запускать множество сервисов.
Разница между Docker и Docker Compose
Docker применяется для управления отдельными контейнерами (сервисами), из которых состоит приложение.
Docker Compose используется для одновременного управления несколькими контейнерами, входящими в состав приложения. Этот инструмент предлагает те же возможности, что и Docker, но позволяет работать с более сложными приложениями.
Docker (отдельный контейнер) и Docker Compose (несколько контейнеров)
Типичный сценарий использования Docker Compose
Docker Compose — это, в умелых руках, весьма мощный инструмент, позволяющий очень быстро развёртывать приложения, отличающиеся сложной архитектурой. Сейчас мы рассмотрим пример практического использования Docker Compose, разбор которого позволит вам оценить те преимущества, которые даст вам использование Docker Compose.
Представьте себе, что вы являетесь разработчиком некоего веб-проекта. В этот проект входит два веб-сайта. Первый позволяет людям, занимающимся бизнесом, создавать, всего в несколько щелчков мышью, интернет-магазины. Второй нацелен на поддержку клиентов. Эти два сайта взаимодействуют с одной и той же базой данных.
Ваш проект становится всё популярнее, и оказывается, что мощности сервера, на котором он работает, уже недостаточно. В результате вы решаете перевести весь проект на другую машину.
К сожалению, нечто вроде Docker Compose вы не использовали. Поэтому вам придётся переносить и перенастраивать сервисы по одному, надеясь на то, что вы, в процессе этой работы, ничего не забудете.
Если же вы используете Docker Compose, то перенос вашего проекта на новый сервер — это вопрос, который решается выполнением нескольких команд. Для того чтобы завершить перенос проекта на новое место, вам нужно лишь выполнить кое-какие настройки и загрузить на новый сервер резервную копию базы данных.
Разработка клиент-серверного приложения с использованием Docker Compose
Теперь, когда вы знаете о том, для чего мы собираемся использовать Docker Compose, пришло время создать ваше первое клиент-серверное приложение с использованием этого инструмента. А именно, речь идёт о разработке небольшого веб-сайта (сервера) на Python, который умеет выдавать файл с фрагментом текста. Этот файл у сервера запрашивает программа (клиент), тоже написанная на Python. После получения файла с сервера программа выводит текст, хранящийся в нём, на экран.
Обратите внимание на то, что мы рассчитываем на то, что вы владеете основами Docker, и на то, что у вас уже установлена платформа Docker.
Приступим к работе над проектом.
▍1. Создание проекта
Для того чтобы построить ваше первое клиент-серверное приложение, предлагаю начать с создания папки проекта. Она должна содержать следующие файлы и папки:
- Файл
docker-compose.yml
. Это файл Docker Compose, который будет содержать инструкции, необходимые для запуска и настройки сервисов. - Папка
server
. Она будет содержать файлы, необходимые для обеспечения работы сервера. - Папка
client
. Здесь будут находиться файлы клиентского приложения.
В результате содержимое главной папки вашего проекта должно выглядеть так:
.
├── client/
├── docker-compose.yml
└── server/
2 directories, 1 file
▍2. Создание сервера
Тут мы, в процессе создания сервера, затронем некоторые базовые вещи, касающиеся Docker.
2a. Создание файлов
Перейдите в папку server
и создайте в ней следующие файлы:
- Файл
server.py
. В нём будет находиться код сервера. - Файл
index.html
. В этом файле будет находиться фрагмент текста, который должно вывести клиентское приложение. - Файл
Dockerfile
. Это — файл Docker, который будет содержать инструкции, необходимые для создания окружения сервера.
Вот как должно выглядеть содержимое вашей папки server/
:
.
├── Dockerfile
├── index.html
└── server.py
0 directories, 3 files
2b. Редактирование Python-файла.
Добавим в файл server.py
следующий код:
#!/usr/bin/env python3
# Импорт системных библиотек python.
# Эти библиотеки будут использоваться для создания веб-сервера.
# Вам не нужно устанавливать что-то особенное, эти библиотеки устанавливаются вместе с Python.
import http.server
import socketserver
# Эта переменная нужна для обработки запросов клиента к серверу.
handler = http.server.SimpleHTTPRequestHandler
# Тут мы указываем, что сервер мы хотим запустить на порте 1234.
# Постарайтесь запомнить эти сведения, так как они нам очень пригодятся в дальнейшем, при работе с docker-compose.
with socketserver.TCPServer(("", 1234), handler) as httpd:
# Благодаря этой команде сервер будет выполняться постоянно, ожидая запросов от клиента.
httpd.serve_forever()
Этот код позволяет создать простой веб-сервер. Он будет отдавать клиентам файл index.html
, содержимое которого позже будет выводиться на веб-странице.
2c. Редактирование HTML-файла
В файл index.html
добавим следующий текст:
Docker-Compose is magic!
Этот текст будет передаваться клиенту.
2d. Редактирование файла Dockerfile
Сейчас мы создадим простой файл Dockerfile
, который будет отвечать за организацию среды выполнения для Python-сервера. В качестве основы создаваемого образа воспользуемся официальным образом, предназначенным для выполнения программ, написанных на Python. Вот содержимое Dockerfile:
# На всякий случай напоминаю, что Dockerfile всегда должен начинаться с импорта базового образа.
# Для этого используется ключевое слово 'FROM'.
# Здесь нам нужно импортировать образ python (с DockerHub).
# В результате мы, в качестве имени образа, указываем 'python', а в качестве версии - 'latest'.
FROM python:latest
# Для того чтобы запустить в контейнере код, написанный на Python, нам нужно импортировать файлы 'server.py' и 'index.html'.
# Для того чтобы это сделать, мы используем ключевое слово 'ADD'.
# Первый параметр, 'server.py', представляет собой имя файла, хранящегося на компьютере.
# Второй параметр, '/server/', это путь, по которому нужно разместить указанный файл в образе.
# Здесь мы помещаем файл в папку образа '/server/'.
ADD server.py /server/
ADD index.html /server/
# Здесь мы воспользуемся командой 'WORKDIR', возможно, новой для вас.
# Она позволяет изменить рабочую директорию образа.
# В качестве такой директории, в которой будут выполняться все команды, мы устанавливаем '/server/'.
WORKDIR /server/
Теперь займёмся работой над клиентом.
▍3. Создание клиента
Создавая клиентскую часть нашего проекта, мы попутно вспомним некоторые основы Docker.
3a. Создание файлов
Перейдите в папку вашего проекта client
и создайте в ней следующие файлы:
- Файл
client.py
. Тут будет находиться код клиента. - Файл
Dockerfile
. Этот файл играет ту же роль, что и аналогичный файл в папке сервера. А именно, он содержит инструкцию, описывающую создание среды для выполнения клиентского кода.
В результате ваша папка client/
на данном этапе работы должна выглядеть так:
.
├── client.py
└── Dockerfile
0 directories, 2 files
3b. Редактирование Python-файла
Добавим в файл client.py
следующий код:
#!/usr/bin/env python3
# Импортируем системную библиотеку Python.
# Она используется для загрузки файла 'index.html' с сервера.
# Ничего особенного устанавливать не нужно, эта библиотека устанавливается вместе с Python.
import urllib.request
# Эта переменная содержит запрос к 'http://localhost:1234/'.
# Возможно, сейчас вы задаётесь вопросом о том, что такое 'http://localhost:1234'.
# localhost указывает на то, что программа работает с локальным сервером.
# 1234 - это номер порта, который вам предлагалось запомнить при настройке серверного кода.
fp = urllib.request.urlopen("http://localhost:1234/")
# 'encodedContent' соответствует закодированному ответу сервера ('index.html').
# 'decodedContent' соответствует раскодированному ответу сервера (тут будет то, что мы хотим вывести на экран).
encodedContent = fp.read()
decodedContent = encodedContent.decode("utf8")
# Выводим содержимое файла, полученного с сервера ('index.html').
print(decodedContent)
# Закрываем соединение с сервером.
fp.close()
Благодаря этому коду клиентское приложение может загрузить данные с сервера и вывести их на экран.
3c. Редактирование файла Dockerfile
Как и в случае с сервером, мы создаём для клиента простой Dockerfile
, ответственный за формирование среды, в которой будет работать клиентское Python-приложение. Вот код клиентского Dockerfile
:
# То же самое, что и в серверном Dockerfile.
FROM python:latest
# Импортируем 'client.py' в папку '/client/'.
ADD client.py /client/
# Устанавливаем в качестве рабочей директории '/client/'.
WORKDIR /client/
▍4. Docker Compose
Как вы могли заметить, мы создали два разных проекта: сервер и клиент. У каждого из них имеется собственный файл Dockerfile
. До сих пор всё происходящее не выходит за рамки основ работы с Docker. Теперь же мы приступаем к работе с Docker Compose. Для этого обратимся к файлу docker-compose.yml
, расположенному в корневой папке проекта.
Обратите внимание на то, что тут мы не стремимся рассмотреть абсолютно все команды, которые можно использовать в docker-compose.yml
. Наша главная цель — разобрать практический пример, дающий вам базовые знания по Docker Compose.
Вот код, который нужно поместить в файл docker-compose.yml
:
# Файл docker-compose должен начинаться с тега версии.
# Мы используем "3" так как это - самая свежая версия на момент написания этого кода.
version: "3"
# Следует учитывать, что docker-composes работает с сервисами.
# 1 сервис = 1 контейнер.
# Сервисом может быть клиент, сервер, сервер баз данных...
# Раздел, в котором будут описаны сервисы, начинается с 'services'.
services:
# Как уже было сказано, мы собираемся создать клиентское и серверное приложения.
# Это означает, что нам нужно два сервиса.
# Первый сервис (контейнер): сервер.
# Назвать его можно так, как нужно разработчику.
# Понятное название сервиса помогает определить его роль.
# Здесь мы, для именования соответствующего сервиса, используем ключевое слово 'server'.
server:
# Ключевое слово "build" позволяет задать
# путь к файлу Dockerfile, который нужно использовать для создания образа,
# который позволит запустить сервис.
# Здесь 'server/' соответствует пути к папке сервера,
# которая содержит соответствующий Dockerfile.
build: server/
# Команда, которую нужно запустить после создания образа.
# Следующая команда означает запуск "python ./server.py".
command: python ./server.py
# Вспомните о том, что в качестве порта в 'server/server.py' указан порт 1234.
# Если мы хотим обратиться к серверу с нашего компьютера (находясь за пределами контейнера),
# мы должны организовать перенаправление этого порта на порт компьютера.
# Сделать это нам поможет ключевое слово 'ports'.
# При его использовании применяется следующая конструкция: [порт компьютера]:[порт контейнера]
# В нашем случае нужно использовать порт компьютера 1234 и организовать его связь с портом
# 1234 контейнера (так как именно на этот порт сервер
# ожидает поступления запросов).
ports:
- 1234:1234
# Второй сервис (контейнер): клиент.
# Этот сервис назван 'client'.
client:
# Здесь 'client/ соответствует пути к папке, которая содержит
# файл Dockerfile для клиентской части системы.
build: client/
# Команда, которую нужно запустить после создания образа.
# Следующая команда означает запуск "python ./client.py".
command: python ./client.py
# Ключевое слово 'network_mode' используется для описания типа сети.
# Тут мы указываем то, что контейнер может обращаться к 'localhost' компьютера.
network_mode: host
# Ключевое слово 'depends_on' позволяет указывать, должен ли сервис,
# прежде чем запуститься, ждать, когда будут готовы к работе другие сервисы.
# Нам нужно, чтобы сервис 'client' дождался бы готовности к работе сервиса 'server'.
depends_on:
- server
▍5. Сборка проекта
После того, как в docker-compose.yml
внесены все необходимые инструкции, проект нужно собрать. Этот шаг нашей работы напоминает использование команды docker build
, но соответствующая команда имеет отношение к нескольким сервисам:
$ docker-compose build
▍6. Запуск проекта
Теперь, когда проект собран, пришло время его запустить. Этот шаг нашей работы соответствует шагу, на котором, при работе с отдельными контейнерами, выполняется команда docker run
:
$ docker-compose up
После выполнения этой команды в терминале должен появиться текст, загруженный клиентом с сервера: Docker-Compose is magic!
.
Как уже было сказано, сервер использует порт компьютера 1234
для обслуживания запросов клиента. Поэтому, если перейти в браузере по адресу http://localhost:1234/, в нём будет отображена страница с текстом Docker-Compose is magic!
.
Полезные команды
Рассмотрим некоторые команды, которые могут вам пригодиться при работе с Docker Compose.
Эта команда позволяет останавливать и удалять контейнеры и другие ресурсы, созданные командой docker-compose up
:
$ docker-compose down
Эта команда выводит журналы сервисов:
$ docker-compose logs -f [service name]
Например, в нашем проекте её можно использовать в таком виде: $ docker-compose logs -f [service name]
.
С помощью такой команды можно вывести список контейнеров:
$ docker-compose ps
Данная команда позволяет выполнить команду в выполняющемся контейнере:
$ docker-compose exec [service name] [command]
Например, она может выглядеть так: docker-compose exec server ls
.
Такая команда позволяет вывести список образов:
$ docker-compose images
Итоги
Мы рассмотрели основы работы с технологией Docker Compose, знание которых позволит вам пользоваться этой технологией и, при желании, приступить к её более глубокому изучению. Вот репозиторий с кодом проекта, который мы здесь рассматривали.
Уважаемые читатели! Пользуетесь ли вы Docker Compose в своих проектах?
Время на прочтение
13 мин
Количество просмотров 126K
«Нам нужен DevOps!»
(самая популярная фраза в конце любого хакатона)
Сначала немного лирики.
Когда разработчик является отличным девопсом, умеющим развернуть своё детище на любой машине под любой OC, это плюс. Однако, если он вообще ничего не смыслит дальше своей IDE, это не минус — в конце концов, деньги ему платят за код, а не за умение его разворачивать. Узкий глубокий специалист на рынке ценится выше, чем средней квалификации «мастер на все руки». Для таких, как мы, «пользователей IDE», хорошие люди придумали Docker.
Принцип Docker следующий: «работает у меня — работает везде». Единственная программа, необходимая для деплоя копии Вашего приложения где угодно — это Docker. Если Вы запустили своё приложение в докере у себя на машине, оно гарантированно с тем же успехом запустится в любом другом докере. И ничего, кроме докера, устанавливать не нужно. У меня, к примеру, на виртуальном сервере даже Java не стоит.
Как работает Docker?
Docker создаёт образ виртуальной машины с установленными в ней приложениями. Дальше этот образ разворачивается как абсолютно автономная виртуальная машина. Запущенная копия образа называется «контейнер». Вы можете запустить на сервере любое количество образов, и каждый из них будет отдельной виртуальной машиной со своим окружением.
Что такое виртуальная машина? Это инкапсулированное место на сервере с операционкой, в которой установлены приложения. В любой операционке обычно крутится большое количество приложений, в нашей же находится одно.
Схему развёртывания контейнеров можно представить так:
Для каждого приложения мы создаём свой образ, а потом разворачиваем каждый контейнер отдельно. Также, можно положить все приложения в один образ и развернуть как один контейнер. Причём, чтобы не разворачивать каждый контейнер отдельно, мы можем использовать отдельную утилиту docker-compose, которая конфигурирует контейнеры и взаимосвязь между ними через отдельный файл. Тогда структура всего приложения может выглядеть так:
Я намеренно не стал вносить базу данных в общую сборку Docker, по нескольким причинам. Во-первых, база данных полностью независима от приложений, которые с ней работают. Это может быть далеко не одно приложение, это могут быть ручные запросы из консоли. Лично я не вижу смысла ставить базу данных в зависимость от сборки Docker, в которой она находится. Поэтому я её и вынес. Впрочем, очень часто практикуется подход, при котором база данных помещается в отдельный образ и запускается отдельным контейнером. Во-вторых, хочется показать, как Docker-контейнер взаимодействует с системами вне контейнера.
Впрочем, довольно лирики, давайте писать код. Мы напишем простейшее приложение на спринге и реакте, которое будет записывать наши обращения к фронту в базу данных, и поднимем всё это через Docker. Структура нашего приложения будет выглядеть так:
Реализовать такую структуру можно разными путями. Мы реализуем один из них. Мы создадим два образа, запустим из них два контейнера, причём, бэкенд будет подключаться к базе данных, которая установлена на конкретном сервере где-то в интернете (да, такие запросы к базе будут ходить не быстро, но нами движет не жажда оптимизации, а научный интерес).
Пост будет разбит на части:
0. Устанавливаем Docker.
1. Пишем приложения.
2. Собираем образы и запускаем контейнеры.
3. Собираем образы и запускаем контейнеры на удалённом сервере.
4. Решаем проблемы с сетью.
0. Устанавливаем Docker
Для того, чтобы установить Docker, нужно зайти на сайт и следовать тому, что там написано. При установка Docker на удалённом сервере будьте готовы к тому, что с серверами на OpenVZ Docker может не работать. Равно как могут быть проблемы, если у Вас не включён iptables. Желательно заводить сервер на KVM с iptables. Но это мои рекомендации. Если у Вас всё заработает и так, я буду рад, что Вы не потратили кучу времени на выяснение, почему не работает, как это пришлось сделать мне.
1. Пишем приложения
Напишем простое приложение с самым примитивным бэкендом на Spring Boot, очень простым фронтендом на ReactJS и базой данных MySQL. Приложение будет иметь Single-Page с одной-единственной кнопкой, которая будет записывать время нажатия по ней в базу данных.
Я рассчитываю на то, что Вы уже умеете писать приложения на буте, но если нет, Вы можете клонировать готовый проект. Все ссылки в конце статьи.
Backend на Spring Boot
build.gradle:
plugins {
id 'org.springframework.boot' version '2.1.4.RELEASE'
id 'java'
}
apply plugin: 'io.spring.dependency-management'
group = 'ru.xpendence'
version = '0.0.2'
sourceCompatibility = '1.8'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
runtimeOnly 'mysql:mysql-connector-java'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
Сущность Log:
package ru.xpendence.rebounder.entity;
import com.fasterxml.jackson.annotation.JsonFormat;
import javax.persistence.*;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.Objects;
/**
* Author: Vyacheslav Chernyshov
* Date: 14.04.19
* Time: 21:20
* e-mail: 2262288@gmail.com
*/
@Entity
@Table(name = "request_logs")
public class Log implements Serializable {
private Long id;
private LocalDateTime created;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
public Long getId() {
return id;
}
@Column
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss.SSS")
public LocalDateTime getCreated() {
return created;
}
@PrePersist
public void prePersist() {
this.created = LocalDateTime.now();
}
//setters, toString, equals, hashcode, constructors
LogController, который будет работать по упрощённой логике и сразу писать в базу данных. Сервис мы опускаем.
package ru.xpendence.rebounder.controller;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import ru.xpendence.rebounder.entity.Log;
import ru.xpendence.rebounder.repository.LogRepository;
import java.util.logging.Logger;
/**
* Author: Vyacheslav Chernyshov
* Date: 14.04.19
* Time: 22:24
* e-mail: 2262288@gmail.com
*/
@RestController
@RequestMapping("/log")
public class LogController {
private final static Logger LOG = Logger.getLogger(LogController.class.getName());
private final LogRepository repository;
@Autowired
public LogController(LogRepository repository) {
this.repository = repository;
}
@GetMapping
public ResponseEntity<Log> log() {
Log log = repository.save(new Log());
LOG.info("saved new log: " + log.toString());
return ResponseEntity.ok(log);
}
}
Всё, как мы видим, очень просто. По GET-запросу мы делаем запись в базу и возвращаем результат.
Файл настроек приложения обсудим отдельно. Их два.
application.yml:
spring:
profiles:
active: remote
application-remote.yml:
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://my-remote-server-database:3306/rebounder_database?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
username: admin
password: 12345
jpa:
hibernate:
ddl-auto: update
show-sql: true
properties:
hibernate.dialect: org.hibernate.dialect.MySQL5Dialect
server:
port: 8099
Как это работает, Вы, наверняка, знаете, сначала Spring сканирует файл application.properties или application.yml — какой найдёт. В нём мы указываем одну-единственную настройку — какой профиль будем использовать. Обычно во время разработки у меня накапливается несколько профилей, и очень удобно их переключать при помощи дефолтного профиля. Далее Spring находит application.yml с нужным суффиксом и пользует его.
Мы указали датасорс, настройки JPA и, что важно, внешний порт нашего бэкенда.
Фронтенд на ReactJS
Фронтенд тоже можно посмотреть в проекте на git, а можно даже не смотреть, а клонировать и запустить.
Отдельную работу фронтенда можно проверить, скачав проект, перейдя в терминале в корневую папку проекта (туда, где лежит файл package.json) и выполнив последовательно две команды:
npm install // устанавливает в проект все необходимые зависимости, аналог maven
npm start // запускает проект
Конечно, для этого Вам нужен установленный Node Package Manager (npm), и это тот самый трудный путь, которого мы избегаем при помощи Docker. Если Вы всё-таки запустили проект, Вы увидите следующее окошко:
Ну да ладно, настало время посмотреть код. Укажу лишь часть, которая обращается к бэкенду.
export default class Api {
_apiPath = 'http://localhost:8099';
_logUrl = '/log';
getResource = async () => {
const res = await fetch(`${this._apiPath}${this._logUrl}`);
if (!res.ok) {
throw new Error(`Could not fetch ${this._logUrl}` +
`, received ${res.status}`)
}
return await res.json();
};
};
Фронтенд работает предсказуемо. Переходим по ссылке, дожидаемся ответа и выводим его на экран.
Стоит акцентировать внимание на следующих пунктах:
- Фронт открыт внешнему миру через порт 3000. Это порт по умолчанию для React.
- Бэк открыт по порту 8099. Мы его задали в настройках приложения.
- Бэк стучится к БД через внешний интернет.
Приложение готово.
2. Собираем образы и запускаем контейнеры
Структура нашей сборки будет следующая. Мы создадим два образа — фронтенд и бэкенд, которые будут общаться друг с другом через внешние порты. Для базы мы не будем создавать образ, мы установим её отдельно. Почему так? Почему для базы мы не создаём образ? У нас есть два приложения, которые постоянно изменяются и не хранят в себе данные. База данных хранит в себе данные, и это может быть результат нескольких месяцев работы приложения. Более того, к данной базе данных может обращаться не только наше бэкенд-приложение, но и многие другие — на то она и база данных, и мы не будем её постоянно пересобирать. Опять же, это возможность поработать с внешним API, чем, безусловно, является подключение к нашей БД.
Сборка front-end
Для запуска каждого приложения (будь то фронт или бэк) потребуется определённая последовательность действий. Для запуска приложения на React нам потребуется сделать следующее (при условии, что у нас уже есть Linux):
- Установить NodeJS.
- Скопировать приложение в определённую папку.
- Проинсталлировать необходимые пакеты (команда npm install).
- Запустить приложение командой npm start.
Именно эту последовательность действий нам и предстоит выполнить в докере. Для этого в корне проекта (там же, где находится package.json) мы должны разместить файл Dockerfile со следующим содержанием:
FROM node:alpine
WORKDIR /usr/app/front
EXPOSE 3000
COPY ./ ./
RUN npm install
CMD ["npm", "start"]
Разберём, что означает каждая строчка.
FROM node:alpine
Этой строчкой мы даём понять докеру, что при запуске контейнера первым делом нужно будет скачать из репозитория Docker и установить NodeJS, причём, самую лёгкую (все самые лёгкие версии популярных фреймворков и библиотек в докере принято называть alpine).
WORKDIR /usr/app/front
В линуксе контейнера будут созданы те же стандартные папки, что и в других линуксах — /opt, /home, /etc, /usr и проч. Мы задаём рабочую директорию, с которой будем работать — /usr/app/front.
EXPOSE 3000
Открываем порт 3000. Дальнейшая связь с приложением, запущенным в контейнере, будет происходить через этот порт.
COPY ./ ./
Копируем содержимое исходного проекта в рабочую папку контейнера.
RUN npm install
Устанавливаем все пакеты, необходимые для запуска приложения.
CMD ["npm", "start"]
Запускаем приложение командой npm start.
Именно этот сценарий будет выполнен в нашем приложении при запуске контейнера.
Давайте сразу соберём фронт. Для этого нужно в терминале, находясь в корневой папке проекта (там, где находится Dockerfile), выполнить команду:
docker build -t rebounder-chain-frontend .
Значения команд:
docker — вызов приложения docker, ну, это вы знаете.
build — сборка образа из целевых материалов.
-t <имя> — в дальнейшем, приложение будет доступно по тегу, указанному здесь. Можно не указывать, тогда Docker сгенерирует собственный тег, но отличить его от других будет невозможно.
. — указывает, что собирать проект нужно именно из текущей папки.
В результате, сборка должна закончиться текстом:
Step 7/7 : CMD ["npm", "start"]
---> Running in ee0e8a9066dc
Removing intermediate container ee0e8a9066dc
---> b208c4184766
Successfully built b208c4184766
Successfully tagged rebounder-chain-frontend:latest
Если мы видим, что последний шаг выполнен и всё Successfull, значит, образ у нас есть. Мы можем проверить это, запустив его:
docker run -p 8080:3000 rebounder-chain-frontend
Смысл этой команды, я думаю, в целом понятен, за исключением записи -p 8080:3000.
docker run rebounder-chain-frontend — означает, что мы запускаем такой-то докер-образ, который мы обозвали rebounder-chain-frontend. Но такой контейнер не будет иметь выхода наружу, ему нужно задать порт. Именно команда ниже его задаёт. Мы помним, что наше React-приложение запускается на порте 3000. Команда -p 8080:3000 указывает докеру, что нужно взять порт 3000 и пробросить его на порт 8080 (который будет открыт). Таким образом, приложение, которое работает по порту 3000, будет открыто по порту 8080, и на локальной машине будет доступно именно по этому порту.
Итак, что мы видим при запуске команды выше:
Mac-mini-Vaceslav:rebounder-chain-frontend xpendence$ docker run -p 8080:3000 rebounder-chain-frontend
> rebounder-chain-frontend@0.1.0 start /usr/app/front
> react-scripts start
Starting the development server...
Compiled successfully!
You can now view rebounder-chain-frontend in the browser.
Local: http://localhost:3000/
On Your Network: http://172.17.0.2:3000/
Note that the development build is not optimized.
To create a production build, use npm run build.
Пусть вас не смущает запись
Local: http://localhost:3000/
On Your Network: http://172.17.0.2:3000/
Так думает React. Он действительно доступен в пределах контейнера по порту 3000, но мы пробросили этот порт на порт 8080, и из контейнера приложение работает по порту 8080. Можете запустить приложение локально и проверить это.
Итак, у нас есть готовый контейнер с фронтенд-приложением, теперь давайте соберём бэкенд.
Сборка back-end.
Сценарий запуска приложения на Java существенно отличается от предыдущей сборки. Он состоит из следующих пунктов:
- Устанавливаем JVM.
- Собираем jar-архив.
- Запускаем его.
В Dockerfile этот процесс выглядит так:
# back
# устанавливаем самую лёгкую версию JVM
FROM openjdk:8-jdk-alpine
# указываем ярлык. Например, разработчика образа и проч. Необязательный пункт.
LABEL maintainer="2262288@gmail.com"
# указываем точку монтирования для внешних данных внутри контейнера (как мы помним, это Линукс)
VOLUME /tmp
# внешний порт, по которому наше приложение будет доступно извне
EXPOSE 8099
# указываем, где в нашем приложении лежит джарник
ARG JAR_FILE=build/libs/rebounder-chain-backend-0.0.2.jar
# добавляем джарник в образ под именем rebounder-chain-backend.jar
ADD ${JAR_FILE} rebounder-chain-backend.jar
# команда запуска джарника
ENTRYPOINT ["java","-jar","/rebounder-chain-backend.jar"]
Процесс сборки образа с включением джарника по некоторым пунктам напоминает таковой для нашего фронта.
Процесс сборки и запуска второго образа принципиально ничем не отличается от сборки и запуска первого.
docker build -t rebounder-chain-backend .
docker run -p 8099:8099 rebounder-chain-backend
Теперь, если у Вас запущены оба контейнера, а бэкенд подключён к БД, всё заработает. Напоминаю, что подключение к БД из бэкенда Вы должны прописать сами, и оно должно работать через внешнюю сеть.
3. Собираем образы и запускаем контейнеры на удалённом сервере
Для того, чтобы всё заработало на удалённом сервере, нам необходимо, чтобы на нём был уже установлен Docker, после чего, достаточно запустить образы. Мы пойдём правильным путём и закоммитим наши образы в свой аккаунт в облаке Docker, после чего, они станут доступны из любой точки мира. Конечно, альтернатив данному подходу, как и всему, что описано в посте, предостаточно, но давайте ещё немного поднажмём и сделаем свою работу хорошо. Плохо, как говорил Андрей Миронов, мы всегда успеем сделать.
Создание аккаунта на хабе Docker
Первое, что Вам предстоит сделать — это обзавестись аккаунтом на хабе Docker. Для этого надо перейти на хаб и зарегаться. Это несложно.
Далее, нам нужно зайти в терминал и авторизоваться в Docker.
docker login
Вас попросят ввести логин и пароль. Если всё ок, в терминале появится уведомление, что Login Succeeded.
Коммит образов на Docker Hub
Далее, мы должны пометить наши образы тэгами и закоммитить их в хаб. Делается это командой по следующей схеме:
docker tag имя образа логин/имя_образа:версия
Таким образом, нам нужно указать имя нашего образа, логин/репозиторий и тэг, под которым наш образ будет закоммичен в хаб.
В моём случае, это выглядело так:
Мы можем проверить наличие данного образа в локальном репозитории при помощи команды:
Mac-mini-Vaceslav:rebounder-chain-backend xpendence$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
xpendence/rebounder-chain-backend 0.0.2 c8f5b99e15a1 About an hour ago 141MB
Наш образ готов к коммиту. Коммитим:
docker push xpendence/rebounder-chain-backend:0.0.2
Должна появиться запись об успешном коммите.
Делаем то же самое с фронтендом:
docker tag rebounder-chain-frontend xpendence/rebounder-chain-frontend:0.0.1
docker push xpendence/rebounder-chain-frontend:0.0.1
Теперь, если мы зайдём на hub.docker.com, мы увидим два закоммиченных образа. Которые доступны откуда угодно.
Поздравляю. Нам осталось перейди к заключительной части нашей работы — запустить образы на удалённом сервере.
Запускаем образы на удалённом сервере
Теперь мы можем запустить наш образ на любой машине с Docker, выполнив всего одну строчку в терминале (в нашем случае, нам надо последовательно выполнить две строчки в разных терминалах — по одной на каждый образ).
docker run -p 8099:8099 xpendence/rebounder-chain-backend:0.0.2
docker run -p 8080:3000 xpendence/rebounder-chain-frontend:0.0.1
У такого запуска есть, правда, один минус. При закрытии терминала процесс завершится и приложение прекратит работу. Чтобы этого избежать, мы можем запустить приложение в «откреплённом» режиме:
docker run -d -p 8099:8099 xpendence/rebounder-chain-backend:0.0.2
docker run -d -p 8080:3000 xpendence/rebounder-chain-frontend:0.0.1
Теперь приложение не будет выдавать лог в терминал (это можно, опять же, настроить отдельно), но и при закрытии терминала оно не прекратит свою работу.
4. Решаем проблемы с сетью
Если Вы всё сделали правильно, Вас, возможно, ожидает самое большое разочарование на всём пути следования этому посту — вполне может так получиться, что ничего не работает. Например, у Вас всё великолепно собралось и заработало на локальной машине (как, например, у меня на маке), но при развёртывании на удалённом сервере контейнеры перестали друг друга видеть (как, например, у меня на удалённом сервере на Linux). В чём проблема? А проблема вот в чём, и я в начале о ней намекал. Как уже было сказано раньше, при запуске контейнера Docker создаёт отдельную виртуальную машину, накатывает туда Linux, и потом в этот Linux устанавливает приложение. Это значит, что условный localhost для запущенного контейнера ограничивается самим контейнером, и о существовании других сетей приложение не подозревает. Но нам нужно, чтобы:
а) контейнеры видели друг друга.
б) бэкенд видел базу данных.
Решения проблемы два.
1. Создание внутренней сети.
2. Вывод контейнеров на уровень хоста.
1. На уровне Docker можно создавать сети, причём, три из них в нём есть по умолчанию — bridge, none и host.
Bridge — это внутренняя сеть Docker, изолированная от сети хоста. Вы можете иметь доступ к контейнерам только через те порты, которые открываете при запуске контейнера командой -p. Можно создавать любое количество сетей типа bridge.
None — это отдельная сеть для конкретного контейнера.
Host — это сеть хоста. При выборе этой сети, Ваш контейнер полностью доступен через хост — здесь попросту не работает команда -p, и если Вы развернули контейнер в этой сети, то Вам незачем задавать внешний порт — контейнер доступен по своему внутреннему порту. Например, если в Dockerfile EXPOSE задан как 8090, именно через этот порт будет доступно приложение.
Поскольку нам нужно иметь доступ к базе данных сервера, мы воспользуемся последним способом и выложим контейнеры в сеть удалённого сервера.
Делается это очень просто, мы убираем из команды запуска контейнера упоминание о портах и и указываем сеть host:
docker run --net=host xpendence/rebounder-chain-frontend:0.0.8
Подключение к базе я указал
localhost:3306
Подключение фронта к бэку пришлось указать целиком, внешнее:
http://<хост_удалённого_сервера:порт_удалённого_сервера>
Если вы пробрасываете внутренний порт на внешний, что часто бывает для удалённых серверов, то для базы нужно указывать внутренний порт, а для контейнера — внешний порт.
Если Вы хотите поэкспериментировать с подключениями, вы можете скачать и собрать проект, который я специально написал для тестирования соединения между контейнерами. Просто вводите необходимый адрес, жмёте Send и в режиме отладки смотрите, что прилетело обратно.
Проект лежит тут.
Заключение
Есть масса способов собрать и запустить образ Docker. Интересующимся советую изучить docker-compose. Здесь мы разобрали лишь один из способов работы с докером. Конечно, такой подход поначалу кажется не таким уж и простым. Но вот пример — в ходе написания поста у меня возникли с исходящими подключениями на удалённом сервере. И в процессе дебага мне пришлось несколько раз менять настройки подключения к БД. Вся сборка и деплой умещались у меня в набор 4 строчек, после ввода которых я видел результат на удалённом сервере. В режиме экстремального программирования Docker окажется незаменим.
Как и обещал, выкладываю исходники приложений:
backend
frontend
🛠️ Стек технологий:
Проект Foodgram
https://github.com/emarpoint/foodgram-project-react
Описание
Cайт Foodgram — онлайн-сервис, на котором пользователи могут публиковать рецепты, подписываться на публикации других пользователей, добавлять понравившиеся рецепты в список «Избранное», а перед походом в магазин скачивать сводный список продуктов, необходимых для приготовления одного или нескольких выбранных блюд. Проект использует базу данных PostgreSQL. Проект запускается в трёх контейнерах (nginx, PostgreSQL и Django) (контейнер frontend используется лишь для подготовки файлов) через docker-compose на сервере. Образ с проектом загружается на Docker Hub.
Пользовательские роли
Гость (неавторизованный пользователь)
Что могут делать неавторизованные пользователи:
- Создать аккаунт.
- Просматривать рецепты на главной.
- Просматривать отдельные страницы рецептов.
- Просматривать страницы пользователей.
- Фильтровать рецепты по тегам.
Авторизованный пользователь
Что могут делать авторизованные пользователи:
- Входить в систему под своим логином и паролем.
- Выходить из системы (разлогиниваться).
- Менять свой пароль.
- Создавать/редактировать/удалять собственные рецепты
- Просматривать рецепты на главной.
- Просматривать страницы пользователей.
- Просматривать отдельные страницы рецептов.
- Фильтровать рецепты по тегам.
- Работать с персональным списком избранного: добавлять в него рецепты или удалять их, просматривать свою страницу избранных рецептов.
- Работать с персональным списком покупок: добавлять/удалять любые рецепты, выгружать файл со количеством необходимых ингридиентов для рецептов из списка покупок.
- Подписываться на публикации авторов рецептов и отменять подписку, просматривать свою страницу подписок.
Администратор
Администратор обладает всеми правами авторизованного пользователя. Плюс к этому он может:
- изменять пароль любого пользователя,
- создавать/блокировать/удалять аккаунты пользователей,
- редактировать/удалять любые рецепты,
- добавлять/удалять/редактировать ингредиенты.
- добавлять/удалять/редактировать теги.
Ресурсы API Foodgram
- Ресурс auth: аутентификация.
- Ресурс users: пользователи.
- Ресурс tags: получение данных тега или списка тегов рецепта.
- Ресурс recipes: создание/редактирование/удаление рецептов, а также получение списка рецептов или данных о рецепте.
- Ресурс shopping_cart: добавление/удаление рецептов в список покупок.
- Ресурс download_shopping_cart: cкачать файл со списком покупок.
- Ресурс favorite: добавление/удаление рецептов в избранное пользователя.
- Ресурс subscribe: добавление/удаление пользователя в подписки.
- Ресурс subscriptions: возвращает пользователей, на которых подписан текущий пользователь. В выдачу добавляются рецепты.
- Ресурс ingredients: получение данных ингредиента или списка ингредиентов.
Установка
В приложения настроено Continuous Integration и Continuous Deployment:
- автоматический запуск тестов,
- обновление образов на Docker Hub,
- автоматический деплой на боевой сервер при пуше в главную ветку main.
Шаблон наполнения env-файла
DB_ENGINE=django.db.backends.postgresql # указываем, что работаем с postgresql
DB_NAME=postgres # имя базы данных
POSTGRES_USER=postgres # логин для подключения к базе данных
POSTGRES_PASSWORD=postgres # пароль для подключения к БД (установите свой)
DB_HOST=db # название сервиса (контейнера)
DB_PORT=5432 # порт для подключения к БД
Создание образа
Запустите терминал. Убедитесь, что вы находитесь в той же директории, где сохранён Dockerfile, и запустите сборку образа:
docker build -t foodgram .
build — команда сборки образа по инструкциям из Dockerfile.
-t foodgram — ключ, который позволяет задать имя образу, а потом и само имя.
. — точка в конце команды — путь до Dockerfile, на основе которого производится сборка..
Развёртывание проекта в нескольких контейнерах
Инструкции по развёртыванию проекта в нескольких контейнерах пишут в файле docker-compose.yaml.
Убедитесь, что вы находитесь в той же директории, где сохранён docker-compose.yaml и запустите docker-compose командой docker-compose up. У вас развернётся проект, запущенный через Gunicorn с базой данных Postgres.
Стек технологий
- asgiref==3.5.0
- autopep8==1.6.0
- certifi==2021.10.8
- cffi==1.15.0
- charset-normalizer==2.0.12
- coreapi==2.3.3
- coreschema==0.0.4
- cryptography==36.0.1
- defusedxml==0.7.1
- Django==2.2.19
- django-colorfield==0.6.3
- django-extra-fields==3.0.2
- django-filter==21.1
- django-rest-framework==0.1.0
- django-templated-mail==1.1.1
- djangorestframework==3.13.1
- djoser==2.1.0
- flake8==4.0.1
- idna==3.3
- importlib-metadata==1.7.0
- itypes==1.2.0
- Jinja2==3.0.3
- MarkupSafe==2.1.0
- mccabe==0.6.1
- oauthlib==3.2.0
- Pillow==9.0.1
- psycopg2-binary==2.8.6
- pycodestyle==2.8.0
- pycparser==2.21
- pyflakes==2.4.0
- PyJWT==2.3.0
- python-dotenv==0.19.2
- python3-openid==3.2.0
- pytz==2021.3
- reportlab==3.6.9
- requests==2.27.1
- requests-oauthlib==1.3.1
- six==1.16.0
- social-auth-app-django==4.0.0
- social-auth-core==4.2.0
- sqlparse==0.4.2
- toml==0.10.2
- typing_extensions==4.1.1
- uritemplate==4.1.1
- urllib3==1.26.8
- zipp==3.7.0
- gunicorn==20.0.4
Примеры
Примеры запросов по API:
- [GET] /api/users/ — Получить список всех пользователей.
- [POST] /api/users/ — Регистрация пользователя.
- [GET] /api/tags/ — Получить список всех тегов.
- [POST] /api/recipes/ — Создание рецепта.
- [GET] /api/recipes/download_shopping_cart/ — Скачать файл со списком покупок.
- [POST] /api/recipes/{id}/favorite/ — Добавить рецепт в избранное.
- [DEL] /api/users/{id}/subscribe/ — Отписаться от пользователя.
- [GET] /api/ingredients/ — Список ингредиентов с возможностью поиска по имени.
Автор: Марецкий Е.
https://github.com/emarpoint
В этом гайде разбираемся, для чего нужен Docker и Docker Compose, что такое контейнеризация и Docker-образы, а также как развернуть простое веб-приложение с использованием PHP-FPM, Nginx и Postgres.
- Что такое Docker
- Как работает Docker
- Как создать свой Docker-образ
- Что такое Docker Compose и как он работает
- Как создать простое веб-приложение с помощью Docker
- Итог
Давайте представим себе разработчика, который приходит на работу в новую компанию. На онбординге тимлид дает ему первое шуточное задание: изучить сайт организации и найти на нем страницу с фотографиями котиков.
Разработчик узнаёт, что сайт компании работает с помощью веб-сервера Nginx, менеджера процессов PHP-FPM и системы управления базами данных Postgres. Теперь программист ищет нужную страницу. Поиск выглядит так:
- Разработчик вводит в браузере адрес сайта
- Браузер запрашивает HTML-страницу с котиками по указанному адресу
- HTTP-сервер Nginx принимает запрос и делегирует создание страницы PHP-FPM
- PHP-FPM запрашивает данные о котиках из базы Postgres, строит HTML-страницу и отдает обратно его серверу Nginx, а тот — клиенту-браузеру
- Разработчик видит страницу с котиками.
Свое первое задание разработчик выполняет на компьютере тимлида, где уже установлен Nginx, PHP-FPM и Postgres. На следующий день ему выдают новый компьютер, на котором этих программ нет.
Чтобы начать работать над сайтом вместе с коллегами, разработчик разворачивает проект, то есть устанавливает и настраивает все необходимое для работы. Он делает это последовательно:
- Устанавливает Nginx
- Устанавливает PHP-FPM и все нужные расширения
- Настраивает совместную работу Nginx и PHP-FPM
- Устанавливает Postgres, создает пользователей, нужные базы и схемы.
Установка идет долго: приходится ждать, пока сначала установится одна программа, потом другая. Сложности добавляет и то, что вся его команда работает над проектом на разных операционных системах: одни на macOS, а другие на Ubuntu или Windows.
Чтобы не терять время, устанавливая программу за программой, разработчик мог бы автоматизировать свои действия с помощью программы Docker. Она разворачивает проект программиста за считанные минуты.
Что такое Docker
Docker — это популярная программа, в основе которой лежит технология контейнеризации. Docker позволяет запускать Docker-контейнеры с приложениями из заранее заготовленных шаблонов — Docker-образов (или по-другому Docker images).
Контейнеризация — это технология, которая помогает запускать приложения изолированно от операционной системы. Приложение как бы упаковывается в специальную оболочку — контейнер, внутри которого находится среда, необходимая для работы.
Простыми словами контейнер — это некая изолированная песочница для запуска ваших приложений.
На картинке видно, что приложение 1 и приложение 2 изолированы как друг от друга, так и от операционной системы.
Что еще может делать Docker:
- Управлять изолированными приложениями
- Ускорять и автоматизировать развертывание приложений
- Доставлять приложения до серверов
- Масштабировать приложения
- Запускать на одном компьютере разные версии одной программы.
Читайте также:
Как читать чужой код: 6 правил, которые стоит помнить разработчику
Как работает Docker
Концепцию программы легче понять на практике. Сначала установим на компьютер Docker и запустим HTTP-сервер Nginx. Для этого введем следующую команду:
docker run -p 8080:80 nginx:latest
Далее откроем браузер и забьем в адресную строку: 127.0.0.1:8080
. Откроется страница приветствия Nginx.
Теперь разберемся подробнее, что происходит, когда мы вводим команду docker run -p 8080:80 nginx:latest
. Она выполняет следующее:
- Скачивает docker-образ — шаблон для создания Docker-контейнера —
nginx:latest
из публичного репозитория Docker Hub (если его не скачивали ранее). Docker-образ содержит все необходимое для запуска приложения: код, среду выполнения, библиотеки, переменные окружения и файлы конфигурации. На странице Nginx в Docker Hub можно найти Docker-образ nginx:latest, где latest — это тег (метка, снимок), который ссылается на самый свежий docker-образ и описывает его. - Запускает Docker-контейнер с помощью Docker-образа
- Пробрасывает порт. Ранее мы объясняли, что процессы в Docker-контейнерах запускаются в изоляции от ОС, то есть все порты между ОС и Docker-контейнером закрыты. Для того, чтобы мы смогли обратиться к Nginx, нужно пробросить порт, что и делает опция
-p 8080:80
, где 80 — это порт Nginx внутри контейнера, а 8080 — порт в локальной сети ОС.
Как создать свой Docker-образ
Теперь попробуем создать свой Docker-образ, взяв за основу nginx:latest
. Docker умеет создавать Docker-образ, читая текстовые команды, которые записаны в файл Dockerfile.
Вот пример простейшего Dockerfile:
FROM nginx:latest
RUN echo 'Hi, we are building a custom docker image from nginx:latest!'
COPY nginx-custom-welcome-page.html /usr/share/nginx/html/index.html
Команда FROM
задает базовый (родительский) Docker-образ и всегда вызывается в первую очередь. Команда COPY
копирует файлы в Docker-контейнер.
С помощью COPY
можно заменить стандартную велком-страницу Nginx на такую страницу:
<!DOCTYPE html>
<html>
<body>
<h1>Welcome to custom Nginx page!</h1>
</body>
</html>
Узнать подробнее об этих и других командах Docker можно в официальной документации.
Теперь, когда мы разобрались, за что отвечают команды, создадим Docker-образ из Dockerfile:
$ docker build -t nginx_custom:latest -f /opt/src/docker-for-kids/dockerFiles/Nginx-custom/Dockerfile /opt/src/docker-for-kids
Sending build context to Docker daemon 139.3kB
Step 1/3 : FROM nginx:latest
latest: Pulling from library/nginx
31b3f1ad4ce1: Pull complete
fd42b079d0f8: Pull complete
30585fbbebc6: Pull complete
18f4ffdd25f4: Pull complete
9dc932c8fba2: Pull complete
600c24b8ba39: Pull complete
Digest: sha256:0b970013351304af46f322da1263516b188318682b2ab1091862497591189ff1
Status: Downloaded newer image **for** nginx:latest
---**>** 2d389e545974
Step 2/3 : RUN echo 'Hi, we are building a custom docker image from nginx:latest!'
---**>** Running **in** 05ffd060061f
Hi, we are building a custom docker image from nginx:latest!
Removing intermediate container 05ffd060061f
---**>** 9ac62be4252a
Step 3/3 : COPY nginx-custom-welcome-page.html /usr/share/nginx/html/index.html
---**>** 704121601a45
Successfully built 704121601a45
Successfully tagged nginx_custom:latest
Поясним, какие команды мы использовали в этом коде:
-t nginx_custom:latest
— это имя будущего Docker-образа,latest
— это тег-f /opt/src/docker-for-kids/dockerFiles/Nginx-custom/Dockerfile
— путь до Dockerfile/opt/src/docker-for-kids
— директория, в контексте которой будет создан Docker-образ. Контекст — это все то, что доступно для команд из Dockerfile при сборке (билде) образа. Процесс создания Docker-образа может ссылаться на любой из файлов в контексте.
Теперь запускаем команду:
$ docker run -p 8080:80 Nginx_custom:latest
Docker-образ готов.
Читайте также:
Как настроить VS Code для разработки на PHP и JavaScript
Что такое Docker Compose и как он работает
С ростом количества Docker-контейнеров их становится труднее поддерживать. Конфигурация каждого контейнера описывается в своем Dockerfile, и их нужно запускать отдельной командой. Это же касается сборки или пересборки контейнеров.
Работу облегчает Docker Compose — это инструмент для описания многоконтейнерных приложений. С его помощью можно собрать один файл, в котором наглядно описываются все контейнеры. Еще Docker Compose позволяет собирать, останавливать и запускать файлы одной командой.
Для описания приложений используется YAML-файл.
version: '3'
services:
nginx:
container_name: nginx-test # имя Docker-контейнера
build: # создать Docker-образ из DockerFile
context: . # путь, в контексте которого будет создан Docker-образ
dockerfile: ./dockerFiles/nginx/Dockerfile # путь до Dockerfile, из которого будет собран Docker-образ
ports: # проброс портов
- "80:80"
networks: # имя сети, к которой будет подключен Docker-контейнер
- test-network
depends_on: # эта программа будет запущена только после того, как запустится сервис под именем php-fpm
- php-fpm
volumes: # монтирование директорий, директория-на-хост-машине: директория-в-докере
- ./:/var/www/hello.dev/
php-fpm:
container_name: php-fpm-test
build:
context: .
dockerfile: ./dockerFiles/php-fpm/Dockerfile
networks:
- test-network
volumes:
- ./:/var/www/hello.dev/
postgres:
container_name: postgres-test
image: postgres:14.1-alpine # тег Docker-образа из https://hub.docker.com/
environment:
postgres_PASSWORD: mysecretpass # переменные окружения, которые использует Docker-контейнер
networks:
- test-network
networks: # явно объявленные сети
test-network:
driver: bridge
Если изображать этот код схематично, то описание приложения выглядит так:
Каждый сервис находится внутри Docker-контейнера. Точкой входа в приложение, как и в случае с тем разработчиком и веб-сайтом компании, является Nginx. Пользователи веб-сайта делают запросы к Nginx, у которого проброшен порт 80.
Разберем еще несколько команд, которые реализует Docker:
network
. Как мы объяснили ранее, каждое приложение в Docker-контейнере находится в изоляции.networks
объединяет все Docker-контейнеры в одну сеть с именем test-network, и это позволяет обращаться к нужному контейнеру по его имени.volumes
— это механизм для хранения данных вне Docker-контейнера, то есть в файловой системе нашей ОС.volumes
решает проблему совместного использования файлов.
Все примеры, а также исходники Dockerfile можно взять из репозитория на GitHub.
Как создать простое веб-приложение с помощью Docker
Создадим простое веб-приложение, которое покажет нам сообщение об успешном подключении к базе данных. Вместо адреса базы данных используем host=postgres
, такое же имя cервиса, как и в YAML-файле. Напомню, что эта возможность появилась благодаря общей сети test-network.
index.php
<?php
try {
$pdo = new \PDO("pgsql:host=postgres;dbname=postgres", 'postgres', 'mysecretpass');
echo "Подключение к базе данных установлено! <br>";
return;
} catch (PDOException $exception) {
echo "Ошибка при подключении к базе данных<br><b>{$exception->getMessage()}</b><br>";
}
PDO
— это интерфейс для доступа к базам данных в PHP. Подробнее об этом можно узнать в официальной документации.
Теперь, чтобы создать все Docker-образы и запустить Docker-контейнеры нужно выполнить:
docker-compose up --build
Выполняем index.php и видим успешное соединение с базой данных.
Веб-приложение для самостоятельного запуска можно найти в репозитории на GitHub.
Итог
Освоив Docker, разработчики могут разворачивать все необходимые им сервисы на каком угодно компьютере. Также эта программа — отличный инструмент для быстрой доставки до серверов, тестирования. Изучить Docker не так тяжело, как может показаться новичкам, но зато это умение значительно сэкономит их время на ручной установке софта. Почитать про Docker подробнее можно на официальном сайте.
The goal of this article is to show how to run multi-container applications using a single command. Compose is a tool for defining and running multi-container Docker applications. With Compose, you use a configuration file (YAML file) to configure your docker containers. Then, with a single command, you create and start all the services (containers) from your configuration. Let me explain this taking an example.
This article expects you to be familiar with docker and have some considerable experience in using it.
Let’s say we have a simple application having two components, flask app and a redis database. I will go by running the complete application with and without using docker-compose tool, which would give your major use of this compose tool.
Creating the project
Create directory gfg_docker_compose holds the our project
$ mkdir gfg_docker_compose
Move to that directory
$ cd gfg_docker_compose
Create the requirements.txt file
gfg_docker_compose/ $ touch requirements.txt
Copy it to the requirements.txt
Python
Create file app.py will have the code for our flask app
gfg_docker_compose/ $ touch app.py
Copy the below code to app.py
Python3
from
flask
import
Flask, request, jsonify
from
redis
import
Redis
app
=
Flask(__name__)
redis
=
Redis(host
=
"localhost"
, db
=
0
, socket_timeout
=
5
,
charset
=
"utf-8"
, decode_responses
=
True
)
@app
.route(
'/'
, methods
=
[
'POST'
,
'GET'
])
def
animals():
if
request.method
=
=
'POST'
:
name
=
request.json[
'name'
]
redis.rpush(
'animals'
, {
'name'
: name})
return
jsonify({
'status'
:
'success'
})
if
request.method
=
=
'GET'
:
return
jsonify(redis.lrange(
'animals'
,
0
,
-
1
))
Explanation:
We are simply accepting two methods GET and POST requests for `/` route. When ever a POST request is done with the name, the name is added at the end of the animals list. For GET request we will return the list of names from animals list.
Create the dockerfile
gfg_docker_compose/ $ touch dockerfile
Copy the below code to the dockerfile
Python3
FROM python:
3.7
.
0
-
alpine3.
8
WORKDIR
/
usr
/
src
/
app
COPY requirements.txt .
/
RUN pip install
-
-
no
-
cache
-
dir
-
r requirements.txt
COPY . .
ENV FLASK_APP
=
app.py
EXPOSE
5000
CMD flask run
-
-
host
=
0.0
.
0.0
Explanation:
We will start with the base image python:3.7.0-alpine3.8. We will copy the requirements.txt file and install all our flask app dependencies. Then we would copy the app.py file in to the container and finally run the flask app.
And now we ready with the docker application.
Without docker-compose tool
To start and use this application without compose tool would be tedious for a multi-container application as you need to remember the complete configuration and use whenever you run the application. Let’s see how it is normally without the compose tool
Now you will have a project tree as
gfg_docker_compose --- app.py --- requirements.txt --- Dockerfile
Now will run and start our redis server container
gfg_docker_compose/ $ docker run --name=redis redis:4.0.11-alpine
redis server is started
So using that command we will pull redis:4.0.11-alpine image and run a redis container. Now our redis has started so you should take it’s container IP address
gfg_docker_compose/ $ docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' redis
Extract redis container IP Address
this gives you a IP address which you need to put it to the host parameter in the app.py.
Now the line looks like in app.py
redis = Redis(host="IPAddress", db=0, socket_timeout=5, charset="utf-8", decode_responses=True)
where IPAddress is the IP address you get from the redis container.
Build the flask application
gfg_docker_compose/ $ docker build -t gfg/flask-app .
Our gfg/flask-app image is successfully built
Wait for some time and the application image will be built
Now we will start our flask app container as well.
Open a new terminal tab and run below command
gfg_docker_compose/ $ docker run -p 5000:5000 gfg/flask-app
Our flask app is started
So using that command we will pull the gfg/flask-app which we have built earlier and run our flask app container. Also, -p is used to map the port 5000 from container to the host.
Finally when you route to the flask app on a browser you should see something like this.
Our application is working
With docker-compose tool
Using docker-compose tool the setup process for multi-container docker applications will become fairly easy. Simply idea behind this is, we will write the complete container configuration in a YAML file called docker-compose.yml file and then with simple commands we can start and stop the application. This method will also help us to share our docker applications easily to other developers, by simply sharing the docker-compose file and the project.
create the docker-compose.yml file
gfg_docker_compose/ $ touch docker-compose.yml
Now the project tree would look like
gfg_docker_compose --- app.py --- requirements.txt --- Dockerfile --- docker-compose.yml
Now copy the below YAML code to docker-compose.yml file.
Python
version:
'3'
services:
app:
build: .
image: gfg
/
flask
-
app
environment:
-
FLASK_ENV
=
development
ports:
-
5000
:
5000
redis:
image: redis:
4.0
.
11
-
alpine
Explanation:
- version: states the version of docker-compose to use, here we are using version 3
- services: holds all our application services (container) configurations.
- app: We have named our flask app as app service, feel free to give it any other name you want.
- build: relative path to the Dockerfile
- image: name of the final docker application image
- environment: list of environment variables
- ports: list of ports to be mapped from the container to the host machine
- redis: name of our redis service
- image: name of the image.
NOTE: Service names app and redis are also the hostname for the services(containers) we run because docker-compose automatically creates a network and adds our containers to that network so every container can recognize other containers by their service name as hostname within that network. So this is the reason we will keep the host parameter in the app.py file to redis itself.
Start the application
gfg_docker_compose/ $ docker-compose up --build
A successful docker-compose up output looks like this
–build is used to explicitly mention to build the images before starting the application.
You can see the application working as below
Our docker application is working
Stop the complete application
gfg_docker_compose/ $ docker-compose down
A successful docker-compose down looks like this
Using the docker-compose tool, we can make the multi-container docker application setup process much faster and easier than the usual way.