Главная / Блог / DOCKER pART 1

Docker.
Основы работы
с контейнерами.

Docker. Основы работы
с контейнерами.

Smartiqa Article docker
Дата: 20 июня 2022
Автор: Михаил Макарик

1. Виртуализация и виртуальные машины

Виртуализация позволяет абстрагироваться от аппаратных средств (реального «железа») для изоляции нескольких вычислительных процессов на одном компьютере. При этом процессе, фактически, происходит следующее:
  1. Имитируется физический объект;
  2. Ресурсы этого объекта отделены от аппаратной части;
  3. Появляется возможность внедрить несколько вычислительных процессов (операционных систем) на одной машине;
  4. Все операции изолированы.

Виртуализация необходима в современной разработке по ряду причин:
  1. Не нужно выделять физические серверы для серверной инфраструктуры под каждый отдельный случай (можно на одном сервере реализовать несколько);
  2. Запуск программ под конкретную операционную систему (так, пользователь Windows без проблем установит программу под macOS и наоборот);
  3. Необходимость организации виртуальных сетей (VPN). Они надежнее и безопаснее, особенно в корпоративных целях. Один из вариантов виртуализации - виртуальные машины.
Виртуальная машина - программная или аппаратная система, позволяющая исполнять программы на домашней платформе (host) в качестве гостевой.
Проще говоря, в виртуальной среде можно установить, например, Linux поверх ОС Windows. Что важно, «операционки» не будут конфликтовать, а гостевая система будет вести себя как реальный компьютер (с железом, сетевым доступом, набором программ). Да, это можно сделать и без виртуализации, но не всем удобно переключаться между ОС при перезагрузке компьютера.

Виртуальные машины обладают рядом свойств:
  1. Инкапсуляция. Виртуальный компьютер, операционная система, программные средства записываются на ваш ПК в виде отдельного набора файлов, которые легко переносятся;
  2. Изоляция. Любой набор виртуальных машин является полностью независимым и не связан с другими;
  3. Совместимость – виртуальные машины способны содержать разнообразные наборы операционных систем, оборудования и программ, и в то же время их легко реализовать на любом ПК;
  4. Независимость от железа. Так как они запускаются в виртуальном окружении, то полностью независимы от устройств домашнего компьютера.

До того, как контейнеры стали популярными, для изоляции приложений использовались именно виртуальные машины (VirtualBox, VMware). Главные их недостатки:
  1. Требуют много места в памяти;
  2. Относительно долго загружаются;
  3. Для их корректной работы необходима идентичность окружения (приходится устанавливать те же операционные системы и версии программ, которые имеются на тестовом оборудовании). Другими словами, следует полностью "клонировать" всю среду образа.

Контейнеры указанных недочетов не имеют, поэтому они настолько широко вошли в сферу разработки.

2. Контейнеры как инструмент разработчика

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

Один и тот же контейнер всегда работает одинаково, независимо от окружения, в котором он активирован. Контейнеры подобны реальным, в которые можно что-то положить, их легко переместить на другую машину. Они изолированы, друг с другом не «перемешиваются». Дополнительные установки не нужны, так как все уже упаковано внутри.
Virtual Machines vs. Containers
Virtual Machines vs. Containers
Поддержка контейнеров встроена в современные операционные системы. Docker упрощает создание и управление этими контейнерами. Таким образом, контейнеры позволяют создавать независимые, стандартизованные пакеты приложений.

В настоящее время практически все операционные системы поддерживают контейнеры: – любая версия Linux; – версия macOS не ниже 10.14; – версия Windows 10 64-bit Pro или выше.

Для Linux дистрибутивов все просто: тут поддержка контейнеризации уже встроена, нужно лишь дополнительно скачать с репозитория Docker Engine. На macOS или Windows нужно установить Docker Desktop. Его нужно запустить, прежде чем приступить к использованию команд Docker'a.

Дополнительно можно завести учетную запись на сайте репозитория контейнеров. Это позволит загружать свои проекты на ресурс и получать к ним доступ как со своей машины, так и в действующих приложениях (непрерывной интеграции и доставки).

3. Образы и контейнеры

Представим ситуацию: вы скачиваете с GitHub некое приложение, написанное на Python. Чтобы оно заработало на вашем компьютере, необходимо дополнительно загрузить требуемую версию интерпретатора языка, а также все зависимости. Далее следует активировать виртуальную среду, и лишь после этого мы получим возможность запустить скрипт.

Эти операции потребуется проделывать для каждой новой машины и проекта, в которых вы захотите использовать скачанный код. Docker предоставляет другой способ:
  1. При помощи документа Dockerfile описываются все зависимости и установки проекта;
  2. Далее создается образ (копия программы и необходимое для ее работы окружение);
  3. Docker из своего репозитория доустанавливает нужные приложения;
  4. Мы получаем возможность запускать контейнеры на основании созданного образа на любом компьютере, не задумываясь о среде и зависимостях.

Итак, фундаментальные понятия, с которыми нужно «подружиться» – образы (Images) и контейнеры (Containers).
Контейнер – отдельная активная единица некоего приложения (мы с ней работаем, используем ее функционал). Контейнер создается на основе образа.
Образ – шаблон, чертеж контейнера (на основе образов может создаваться любое количество контейнеров). Здесь содержится код и все необходимые настройки и дополнения.
Наглядность связи между этими категориями представлена на рисунке:
Docker: images and containers
Docker: images and containers
Важно

В рамках данной статьи мы взаимодействуем с Docker на базе ОС Windows, однако обратите внимание, что используемые команды будут работать во всех системах.
Начнем работу с Docker (на Windows должно быть запущено приложение Docker Desktop). Для этого нам потребуется образ. Они бывают следующими:
  1. Официальные образы из Docker Hub (здесь можно найти «шаблоны» под любые цели: Python, NodeJS, PostgreSQL, Ubuntu и др.);
  2. Образы, размещенные другими программистами или командами, к которым мы имеем доступ (также размещаются на Docker Hub в большинстве случаев);
  3. Наши собственные образы, которые мы создавали ранее.

Скачаем первый образ из официального репозитория. Воспользуемся последней версией Python. Для этого нам понадобятся 2 команды: pull и run (применять их будем в терминале):
  1. pull – скачивает образ;
  2. run – скачивает образ и запускает его.
Команды: docker pull, docker run
Команды: docker pull, docker run
Пример – Терминал
> docker run python
Unable to find image 'python:latest' locally
latest: Pulling from library/python
d960726af2be: Pulling fs layer
e8d62473a22d: Pull complete
В консоли выведется сообщение о том, что требуемого образа на нашем ПК нет и начнется его скачивание с Docker Hub. После скачивания создается контейнер, который сразу же закроется, так как он не выполняет никакую команду. Такое поведение характерно для большинства контейнеров.

Проверить список имеющихся на компьютере контейнеров можно командой ps -a, а чтобы просмотреть только запущенные, убираем флаг a.
Пример – Терминал
> docker ps
(результатов не увидим, так как нет запущенных контейнеров)
> docker ps -a
CONTAINER ID   IMAGE     COMMAND     CREATED          STATUS                      PORTS     NAMES
5e0356b58ac9   python    "python3"   15 minutes ago   Exited (0) 15 minutes ago             unruffled_moser
Как видно, 15 минут назад запустили контейнер Python с автоматическим присвоением имени unruffled_moser, который сразу же был остановлен, так как не выполнял никаких действий.

В данном случае такое поведение контейнера нам не очень интересно. Запустим образ в интерактивном режиме, чтобы поработать с консолью Python.
Пример – Терминал
> docker run -it python
Python 3.9.5 (default, May 10 2021, 15:26:36) 
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> os.name
'posix'
>>> os.listdir()
['tmp', 'bin', 'root', 'run', 'opt', 'media', 'etc', 'sys', 'boot', 'proc', 'sbin', 'mnt', 'lib64', 'var', 'usr', 'lib', 'dev', 'srv', 'home', '.dockerenv']
>>> exit()
> docker ps -a
CONTAINER ID   IMAGE     COMMAND     CREATED          STATUS                      PORTS     NAMES
82984f732585   python    "python3"   3 minutes ago    Exited (0) 14 seconds ago             frosty_meitner
5e0356b58ac9   python    "python3"   31 minutes ago   Exited (0) 31 minutes ago             unruffled_moser
В примере мы активировали контейнер Python и поработали в нем. После остановки на компьютере образовалось уже 2 контейнера. При последующих взаимодействиях с шаблоном в памяти будут накапливаться старые контейнеры.

Избежать такого поведения можно тремя способами:
  1. Перезапускать ранее активированный контейнер (доступ к нему получаем по ID или имени);
  2. Удалять ненужные контейнеры (через команду rm);
  3. Запускать контейнер в режиме автоматического стирания после работы с ним (флаг --rm)
Пример – Терминал
Удалим ранее созданный контейнер с именем «unruffled_moser»
> docker rm unruffled_moser
unruffled_moser
Запустим заново контейнер с идентификатором «82984f732585»
 > docker start 82984f732585
82984f732585
Активируем новый контейнер с автоматическим удалением после завершения работы с ним (он запустится под случайным именем и сразу же удалится, так как ничего не делает).
Пример – Терминал
> docker run --rm python
В некоторых ситуациях возможно накопление большого числа неиспользуемых контейнеров, удалять которые поодиночке не очень удобно. В таких случаях мы может стереть их сразу все.
Пример – Терминал
> docker rm -f $(docker ps -aq)
82984f732585
5e0356b58ac9
> docker ps -a
(В списке не окажется ни одного контейнера)
Флаг -f удалит в том числе и запущенные контейнеры, а команда ps -aq выведет все идентификаторы. Другими словами, мы получили перечень ID всех контейнеров и удалили их.

Docker позволяет получить список имеющихся образов, а также удалить ненужные. Установим для начала alpine (минималистичная версия Linux), а потом удалим ее.
Пример – Терминал
> docker pull alpine
> docker images
REPOSITORY   TAG       IMAGE ID       CREATED        SIZE
python       latest    a6a0779c5fb2   44 hours ago   886MB
alpine       latest    6dbb9cc54074   4 weeks ago    5.61MB
> docker rmi alpine
> docker images
REPOSITORY   TAG       IMAGE ID       CREATED        SIZE
python       latest    a6a0779c5fb2   44 hours ago   886MB
После совершения всех операций на ПК остался только образ Python.

Еще одна полезная команда – запуск контейнера под нужным именем.
Пример – Терминал
Задаем имя контейнеру
> docker run --name my_py python
Перезапускаем контейнер с именем «my_py»
> docker start my_py
Удостоверяемся, что все сработало
> docker ps -a
CONTAINER ID   IMAGE     COMMAND     CREATED          STATUS                     PORTS     NAMES
114fd2a873d9   python    "python3"   10 seconds ago   Exited (0) 7 seconds ago             my_py

Читайте также

4. Создание собственных образов. Проброс портов.

Не под все наши задачи существуют образы. Более того, создание собственных приложений и их последующая докеризация - довольно частая практическая ситуация.

На примере несложного проекта на Node.js рассмотрим формирование личного образа и его запуск (знать Node.js не обязательно).

Разработанный сайт делает простую операцию: отражает текущую цель пользователя. При желании, ее можно поменять. Структура проекта такова:
Структура проекта
--- main.js
--- package.json
--- css      
        |---- style.css
Содержимое файлов:
1. Файл main.js
const express = require('express');
const bodyParser = require('body-parser');

const spa = express();

let myGoal = 'Learn Docker!';

spa.use(
  bodyParser.urlencoded({
    extended: false,
  })
);

spa.use(express.static('css'));

spa.get('/', (req, res) => {
  res.send(`
    <!DOCTYPE html>
    <html lang="ru">
      <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <link rel="stylesheet" href="style.css">
        <title>Моя цель</title>
      </head>
      <body>
        <section>
          <h2>Моя главная цель</h2>
          <h3>${myGoal}</h3>
        </section>
        <form action="/save-goal" method="POST">
          <div class="form">
            <label>Определить новую цель</label>
            <input type="text" name="goal">
          </div>
          <button>Подтвердить</button>
        </form>
      </body>
    </html>
  `);
});

spa.post('/save-goal', (req, res) => {
  const newGoal = req.body.goal;
  myGoal = newGoal;
  res.redirect('/');
});

spa.listen(80);
2. Файл package.json
{
  "name": "site-js",
  "version": "1.1.1",
  "description": "Первый проект на Node и Docker",
  "main": "main.js",
  "author": "Your Name",
  "license": "X11",
  "dependencies": {
    "express": "^4.17.1",
    "body-parser": "1.19.0"
  }
}
3. Файл style.css
html {
  font-family: cursive;
}

body {
  margin: 0;
}

section,
form {
  padding: 1rem;
  border-radius: 14px;
  box-shadow: 0 3px 6px rgba(165, 73, 73, 0.26);
  margin: 2rem auto;
  max-width: 35rem;
}

.form {
  margin: 0.4rem 0;
}

input {
  font: inherit;
}

input,
label {
  display: block;
}

label {
  font-weight: bold;
  margin-bottom: 0.5rem;
}

button {
  background-color: #421070;
  border: 1px solid #0d0316;
  color: white;
  cursor: pointer;
  padding: 0.6rem 1.4rem;
}

button:hover,
button:active {
  background-color: #8b2399;
  border-color: #1c0542;
}
Если у вас не установлен пакет Node.js, то, естественно, сайт не запустится. Исправим ситуацию с помощью Docker. Для формирования собственного образа в папке проекта нужно создать файл Dockerfile без расширения. В него внесем следующую информацию:
Dockerfile
FROM node: 16-alpine
WORKDIR /site
COPY package.json /site
RUN npm install
COPY . /site
EXPOSE 80
CMD ["node", "main.js"]
Рассмотрим приведенные инструкции:
Теперь можно создать собственный образ с проектом (сразу зададим ему имя goal-site).
Пример – Терминал
> docker build . -t goal-site
[+] Building 39.1s (11/11) FINISHED
> docker images
goal-site    latest    04876a934435   5 minutes ago   131MB
Итак, мы успешно создали образ. Точка после команды build обозначает текущий каталог (следовательно, берем Dockerfile из этой папки). Если попробовать запустить контейнер, то консоль перейдет в режим ожидания (мы ничего в ней больше не сможем сделать), а при наборе в браузере адреса localhost страница не отобразится (хоть он по умолчанию и прослушивает порт 80).

Решим обе эти проблемы:
  1. Чтобы процесс не занимал консоль его удобно запускать в несвязанном режиме (detached, с помощью флага -d);
  2. Для доступа к порту 80 внутри контейнера нужно указать, с какого конкретно порта на нашей машине мы будем туда заходить.
Пример – Терминал
> docker run -d -p 5115:80 goal-site
Теперь открываем браузер, вводим адрес localhost:5115 и наслаждаемся рабочим приложением.

Остался лишь один вопрос: как закрыть запущенный контейнер? Требуется применить команду stop и указать его имя или идентификатор.
Пример – Терминал
Выводим список запущенных контейнеров
> docker ps
CONTAINER ID   IMAGE       COMMAND                  CREATED         STATUS         PORTS                                   NAMES
82b299f23831   goal-site   "docker-entrypoint.s…"   3 minutes ago   Up 3 minutes   0.0.0.0:5115->80/tcp, :::5115->80/tcp   confident_jang
Останавливаем по имени «confident_jang»
> docker stop confident_jang
confident_jang
Теперь сервер не доступен (что можно проверить в браузере).
Таким образом, Docker стандартизирует среду разработки и позволяет запускать приложения в едином окружении на любом компьютере. На основании образов (как готовых, так и личных) можно активировать требуемое количество контейнеров. И для этого не нужно думать ни о типе операционной системы, ни о зависимостях.
20 ИЮНЯ / 2022
Как вам материал?

Читайте также