Контейнеры Docker полностью опущены

В моей статье Создание повторяемых сред разработки я показал, как использую Vagrant в качестве оркестратора и VirtualBox в качестве поставщика виртуальных машин для создания согласованных сред разработки для моих студентов и студентов. команды разработчиков. Это работало очень хорошо, пока Apple не выпустила свои новые Mac 2020 года с чипами Apple M1 Silicon, основанными на архитектуре ARM. Я выбрал VirtualBox, потому что он был бесплатным и поддерживает Mac, Linux и Windows, но работает только на компьютерах Intel (архитектура x86_64), а Apple Silicon - это база ARM (архитектура aarch64). Как оказалось, 8 моих студентов пришли на весенний семестр 2021 года с Mac Apple M1, а это означало, что все мои лабораторные работы, основанные на VirtualBox, не собирались им работать. Мне нужно другое решение, и оно нужно было быстро.

Докер на Apple Silicon

Я слышал, что Docker выпустил предварительную техническую версию Docker Desktop для Mac, работающего на Apple Silicon. Я также вспомнил, что Vagrant поддерживает Docker в качестве провайдера. Это означает, что Vagrant может контролировать подготовку контейнеров Docker так же, как он управляет VirtualBox для подготовки виртуальных машин. Это в некотором роде уникальный вариант использования Docker, потому что цель Docker - предоставить согласованную, неизменяемую среду выполнения; не следует рассматривать как виртуальную машину. Как и в случае со всеми технологиями, всегда есть варианты использования, выходящие за рамки первоначального намерения, и я собирался узнать, был ли этот вариант использования жизнеспособным.

Концепция использования Docker в качестве среды разработки не нова. Фактически, если вы используете Visual Studio Code, существует расширение под названием Удаленные контейнеры, которое позволяет вам разрабатывать в контейнерах. Я планирую рассказать об этом в следующей статье. Разница здесь в том, что обычно вы используете команду docker exec для установки оболочки внутри контейнера, но мне нужно, чтобы эти контейнеры вели себя точно так же, как виртуальная машина (ВМ), потому что я не хотел, чтобы студенты или разработчики с компьютерами Mac Apple M1 имели отличается от опыта на компьютере Intel Mac или Windows. Это означает, что мне нужно было заставить ssh работать с моим контейнером, потому что именно так vagrant ожидает установить оболочку внутри виртуальной машины.

Принуждение контейнера вести себя как виртуальная машина

Каждый скажет вам никогда не устанавливать демон ssh (sshd) внутри контейнера Docker. Фактически, я один из таких людей. Обычно этого не следует делать при использовании контейнеров по их прямому назначению - обеспечению неизменяемой среды выполнения. Это сделало бы их изменяемыми и менее безопасными. Но это другое. Это контейнеры разработки.

Была еще одна проблема, которую нужно было преодолеть. Контейнеры Docker обычно не запускают систему init linux, как виртуальные машины, потому что контейнеры предназначены для запуска только одного процесса, но многие инструменты, необходимые для установки, ожидали системы инициализации. Поскольку я использовал Ubuntu, мне нужно запустить systemd (систему инициализации Ubuntu) в качестве первого процесса (PID 1), чтобы контейнеры действительно работали как виртуальная машина. Проще сказать, чем сделать.

Docker очень затрудняет использование systemd внутри контейнера, но, к счастью для меня, Мэтью Варман уже придумал эту часть для CentOS и был чрезвычайно полезен, заставив меня работать с Ubuntu. Под его руководством я смог создать образ Docker Ubuntu 20.04, который был адаптирован для моих целей. Вы можете использовать этот образ в качестве бродячего провайдера для запуска ваших собственных контейнеров Ubuntu Docker, которые ведут себя как виртуальные машины при разработке, конкурируют с системой инициализации.

Создание образа Docker с несколькими архитектурами

Последним препятствием было создание образа Docker, который работал бы на компьютерах Intel и ARM, чтобы мои студенты и группы разработчиков могли использовать его на любой платформе. Это оказалось на удивление проще, чем я думал. Я даже смог создать образ Docker на моем Intel Mac, который будет работать как на Intel, так и на Apple Silicon Mac, используя экспериментальную функцию Docker под названием buildx.

Это команды, которые я использовал для создания образа Docker моего бродячего провайдера для архитектуры amd64 и arm64.

Сначала вы должны подготовить buildx строитель для использования:

$ export DOCKER_BUILDKIT=1
$ docker buildx create --use --name=qemu
$ docker buildx inspect --bootstrap

Это создает buildx контейнер, который сделает всю работу за вас. Затем, когда вы создаете свой образ, вы должны указать платформы, которые вы хотите создать, и отправить их в docker hub (или другой реестр), чтобы получить образ:

$ docker buildx build --platform linux/amd64,linux/arm64 --push --tag rofrano/vagrant-provider:ubuntu .

Приведенная выше команда использует QEMU для создания многоплатформенного образа, который будет работать как на Intel (amd64), так и на ARM (arm64), и передать его в мою учетную запись концентратора докеров. Если вам нужно поддерживать больше платформ, вы можете просто добавить их в параметр --platform через запятую.

Окончательное решение

Вы можете найти окончательный образ Docker здесь, на Docker Hub: rofrano / vagrant-provider. Если вы планируете использовать его с Vagrant, нет необходимости извлекать его из Docker Hub, потому что это произойдет автоматически, когда vagrant обработает ваш Vagrantfile.

Вот образец мульти-провайдера Vagrantfile, который может использовать VirtualBox или Docker в качестве провайдера с vagrant:

Vagrant.configure("2") do |config|
  config.vm.box = "ubuntu/bionic64"
  config.vm.hostname = "ubuntu"
  # Provider for VirtualBox
  config.vm.provider :virtualbox do |vb|
    vb.memory = "1024"
    vb.cpus = 2
  end
  # Provider for Docker
  config.vm.provider :docker do |docker, override|
    override.vm.box = nil
    docker.image = "rofrano/vagrant-provider:ubuntu"
    docker.remains_running = true
    docker.has_ssh = true
    docker.privileged = true
    docker.volumes = ["/sys/fs/cgroup:/sys/fs/cgroup:ro"]
  end
  # Provision Docker Engine and pull down PostgreSQL
  config.vm.provision :docker do |d|
    d.pull_images "postgres:alpine"
    d.run "postgres:alpine",
       args: "-d -p 5432:5432 -e POSTGRES_PASSWORD=postgres"
  end
end

Чтобы использовать vagrant с Docker вместо VirtualBox, просто укажите параметр --provider с docker в качестве поставщика:

$ vagrant up --provider=docker

Это вызовет контейнер, который ведет себя как виртуальная машина на компьютерах Intel или ARM. Вы могли заметить, что я добавил блок подготовки для Docker в Vagrantfile. Да, верно, мы запускаем Docker внутри нашего контейнера Docker, чтобы студенты и разработчики могли работать с Docker в виртуальной среде, как мы это делаем с настоящими виртуальными машинами. В этом примере мы запускаем PostgreSQL в контейнере Docker для разработки.

Чтобы попасть внутрь этой среды разработки, вы используете ту же команду vagrant, что и при разработке с помощью виртуальной машины:

$ vagrant ssh

Это будет ssh в Docker, как если бы это была виртуальная машина.

Заключение

Использование Docker в качестве поставщика Vagrant позволило мне создать согласованные среды разработки для моих студентов и команд разработчиков независимо от того, есть ли у них компьютер Intel или компьютер на базе ARM, например, новые компьютеры Apple M1 Silicon Mac. Получение базового образа Docker для использования systemd в качестве точки входа позволяет контейнеру вести себя так же, как виртуальная машина. У нас даже есть Docker, работающий внутри контейнера, чтобы мы могли использовать его для баз данных и другого программного обеспечения во время разработки. Другие решения позволяют вести разработку в контейнерах без бродяг, но мы оставим это на будущее.