Виртуальные машины и контейнеры


Виртуальные машины

Виртуальные машины — программы, которые симулируют поведение другого компьютера.

Зачем:

  • Работаете на одной ОС, хотите потестировать, как код работает на другой ОС…
  • … или вообще на другой архитектуре (например, на ARM).
  • Нужна программа, которая работает только на другой ОС.
  • Хотите потрогать вирус, не давая ему доступ к своим личным файлам.

Термины

Host: (“хозяин”) — аппарат, в котором запущена виртуальная машина.

Guest: (“гость”) — аппарат, симулирующийся виртуальной машиной.

Input capture: (“захват ввода”) — неосторожный щелчок мышью внутри окна виртуальной машины иногда её “захватывает”; иногда и клавиатура тоже захвачена целиком, так что сочетания клавиш в host-системе перестают работать.

Snapshot: (“слепок”) — сохранённое состояние виртуальной машины.


Примеры


Примеры

QEMU

QEMU — популярный механизм виртуализации.

  • Производительный;
  • Бесплатный для любых целей;
  • Поддерживает много всяких гостевых и хостовых платформ;
  • Нужно читать документацию, чтобы разобраться.

Лайфхак: в Линуксе можно запускать в терминале с флагом -curses.

См. spice-vdagent для более хорошей интеграции хоста с гостем.



KVM

KVM (Kernel-based Virtual Machine) — встроенный в Linux механизм виртуализации.

QEMU обычно работает поверх него.


Примеры

VirtualBox

VirtualBox — разработанный Oracle механизм виртуализации.

  • Настройка щёлканьем мышью;
  • Бесплатная для личного использования;
  • Есть специальные драйверы для Windows-гостя;
  • Поддерживает x86 и x86_64 как гостей и хостов.
  • Если не для личного использования, тщательно читайте лицензию.


Другие примеры:

  • Android Emulator,
  • iOS Simulator,
  • DosBox.

Docker

Проблемы с поставкой программ:

  • Программы зависят от библиотек. Иногда от конкретных версий.
  • Иногда программы опираются на особенности ОС…
  • … или размещения файлов.

Как сделать установщик для программы так, чтобы она точно заработала или хотя бы сообщила, что не так?

  • Строгие разработческие практики: отслеживание используемых зависимостей и предположений.
  • Регулярная проверка работоспособности в разных конфигурациях.
  • Автоматический анализ программ на предмет неявных зависимостей.

Причем тут Docker?

  • Docker-контейнеры не нужно долго настраивать, как виртуальные машины.
    Альтернатива: nix-shell.

  • Docker-контейнер можно удалить или создать одной командой.
    Альтернатива: nix-shell.

  • Не надо мучиться при поисках/сборке библиотек.
    Альтернатива: пакетный менеджер.

  • Можно не перенастраивать сервер, если сменились требования: обновил Docker-образ — всё заработало как надо.
    Альтернативы для этого: Puppet, Ansible, Chef или решения, встроенные в механизмы оркестрации типа Kubernetes.


Контейнеры

Container (контейнер) — легковесный аналог виртуальной машины.

docker container — команды для управления контейнерами.

docker container ls --all их перечисляет.

На каждый запуск сервиса Docker создаёт новый контейнер.

Пример: docker container run -it archlinux bash — запустить (run) новый контейнер, основанный на образе Arch Linux, с “сервисом” bash в терминале (-t), к которому мы тут же подключаемся интерактивно (-i).

Изменения, внесённые в контейнере, не сохраняются в образе. По соглашению, в контейнере не должно происходить интересных изменений, так как в каждом контейнере обычно запущен один сервис.


Образы

Image (образ) — состояние docker-контейнера.

docker image — команды для управления образами.

docker image ls их перечисляет.


Существует коллекция docker-образов на любой вкус: https://hub.docker.com/.


У образа есть название и тег.

docker pull название:тег выкачивает образ локально.


Популярные образы

https://learn.g2.com/best-docker-containers-repository

Полезные команды

  • docker container diff C — перечислить изменения, внесённые контейнером .
  • docker container commit C — создать новый образ на основе состояния контейнера .
  • docker container attach C — подключить свой терминал к контейнеру .
  • docker container exec C cmd — выполнить команду cmd в контейнере . Её stdin и stdout подключены к вызывающей консоли.
  • docker container create I cmd — создать контейнер на основе образа , выполняющий команду cmd.
  • docker run -it I — интерактивно запустить команду по умолчанию в новом контейнере на основе . Мораль №1: есть команда по умолчанию; мораль №2: в некоторых командах можно опускать container или image.

Сложный пример

docker run \
  --name some-nginx \
  -v /some/content:/usr/share/nginx/html:ro \
  -p 8080:80 \
  -d \
  nginx

Запустить новый контейнер на основе образа nginx, назвать его some-nginx, дать ему доступ на чтение (ro) к директории /some/content/, которую внутри контейнера будет видно по пути /usr/share/nginx/html, пробросить порт 8080 на хостовой машине на порт 80 на гостевой; запускать в фоне (-d).

Запустится веб-сервер nginx.

(”\” ровно в конце строки в shell, C и других языках означает, что следующая строка считается продолжением данной)


Мусор

Каждый вызов docker run создаёт новый контейнер.

Если их воспринимать как черновики, то место на диске можно сберечь, если пользоваться командой docker container prune, уничтожающей остановленные контейнеры.


Dockerfile

  • Чтобы создать нетривиальный docker-образ, не стоит собирать его с нуля или вносить изменения руками и делать docker commit: так будет сложно понять, как воссоздать образ заново в случае потери или смены требований. Лучше воспользоваться Dockerfile — файлом, описывающим программно, как создать образ. См. https://docs.docker.com/engine/reference/builder/.
  • Выполнение каждой команды в Dockerfile сгенерирует новый слой — набор изменений относительно предыдущего слоя. Если на шаге произошла ошибка (или шаг в Dockerfile изменился), шаги не будут выполняться заново.
  • На самом деле, при создании контейнер заводит новый слой относительно своего образа, а docker commit превращает схлопывает этот слой и образ в новый образ.

Пример Dockerfile

FROM ubuntu:18.04
COPY poems /root/poems
ENV EDITOR vim
RUN apt-get update && apt-get install -y \
    git \
    vim \
    && rm -rf /var/lib/apt/lists/*
CMD $EDITOR /root/poems
  • В качестве основы взять образ ubuntu:18.04;
  • Скопировать файл poems по пути /root/poems в образе;
  • Записать vim в переменную EDITOR;
  • Установить git и vim;
  • Назначить в качестве команды по умолчанию vim /root/poems.

Языки и виртуальные машины

Виртуальные машины также используют, чтобы “упростить” компьютер, для которого пишем код.

Как следствие, значительно снизить когнитивную нагрузку на человека, которому приходится писать код.

Такие виртуальные машины часто:

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

Хотя это и не всегда необходимо, некоторрые языки справляюстя и так: Swift, C, Rust, Go и пр., Haskell.


Примеры ВМ для языков

  • Java Virtual Machine, JVM — пожалуй, самый распространённый в жизни пример,
  • CPython — не только интерпретатор Python, но и полноценная виртуальная машина,
  • .NET Framewok — аналог JVM для C# от Microsoft,
  • low level virtual machine, llvm — сильно упрощает написание компиляторов: если написали компилятор вашего языка в llvm, то код можно будет скомпилировать хоть для микроволновки;
  • v8 engine и другие движки для JavaScript — живут в браузере, исполняют js на стороне клиента;
  • Bogdan’s Erlang Abstract Machine, BEAM — виртуальная машина для erlang, вместе с ERTS дают хитрые гарантии на создание дешёвых процессов с удобной коммуникацией;