Pull to refresh
2717.77
RUVDS.com
VDS/VPS-хостинг. Скидка 15% по коду HABR15

Использование игрового ПК с Windows в качестве (Linux) Docker-хоста

Reading time11 min
Views11K
Original author: J. Austin Hughey


Docker Desktop – это прекрасный обслуживаемый способ использования Docker в MacOS или Windows, но для нестандартных сценариев он оставляет желать лучшего.

И недавно я столкнулся с одним таким сценарием, который для вас может показаться относительно типичным. Я занимаюсь разработкой под MacOS, но поскольку у моего MacBook Pro всего 16ГБ оперативной памяти, мне захотелось использовать свой игровой ПК, у которого её 32ГБ, в качестве удалённого Docker-хоста. Сколько нас хакеров, нердов и гиков в течение дня трудится (обычно) на ноутбуках MacBook, а после работы садится за кастомные ПК с Windows, чтобы пострелять пришельцев?

Тут вы можете подумать, что всё это довольно знакомо, а значит и поставленную цель достичь будет несложно. Как бы не так. Оказалось, что заставить Docker работать в качестве хоста локальной сети в Windows весьма проблематично – для этого придётся прибегнуть к ряду специфических приёмов.
Дисклеймер: я отнюдь не являюсь администратором Windows. Мне хорошо известно, что сегодня эта ОС позволяет реализовывать почти всё то же, что системы семейства *nix в плане конфигурации, настройки сервисов и так далее. Но я полностью признаю, что не знаком с современными лучшими техниками для реализации этого.
Я также не рекомендую использовать описываемую мной далее конфигурацию в любых критически важных проектах или продакшене.

▍ В двух словах


  1. Установите WSL 2, затем в нём установите в качестве демона Docker.
  2. Настройте этого демона на прослушивание 0.0.0.0:2375.
  3. Тут вы поймёте, что WSL принудительно привязывает его к localhost, а не к 0.0.0.0, что бы вы ни делали.
  4. Подключите TCP-прокси, выполняющийся в пользовательском пространстве Windows, чтобы привязаться к 0.0.0.0:2375 и использовать его для переброса трафика на localhost:2375.
  5. Проверьте возможность подключения с внешнего хоста.

▍ Docker Desktop: чего делать нельзя


Docker Desktop предлагает элементарный способ сделать вашу машину Windows хостом Docker (по крайней мере, так может показаться): простая галочка в пункте конфигурации. Можно подумать, что этого вполне достаточно, но здесь есть подвох: Docker будет привязан только к Localhost:2375, а не к 0.0.0.0:2375. То есть использование данной опции сделает Docker доступным только на локальной машине, но никак не для других узлов вашей сети.
Примечание: Docker Desktop содержит эту функциональность для того, чтобы делать текущие версии демона доступными для легаси CLI-клиентов docker. Вот только для наших целей это оказывается бесполезным.
Поначалу я этого не понимал и использовал вышеназванную галочку, безуспешно пытаясь обратиться к Docker с другой машины сети. В стремлении понять, что я делаю не так, я заглянул в Windows Resource Monitor. Там я обнаружил, что приложение Docker Desktop прослушивает порт 2375, но привязано только к localhost, в связи с чем не может получать трафик из других узлов сети.

После этого я сделал то, что сделал бы любой рассудительный человек – спросил Google. В одной из найденных мной статей по настройке Docker говорилось, что можно управлять этим поведением, изменяя конфигурацию JSON, представленную в приложении Docker Desktop. Так что я добавил в конфигурацию Docker следующее:

{  "hosts": ["tcp://0.0.0.0:2375"]
}

Затем я отключил фаервол в Windows и антивирусном ПО, просто чтобы исключить этот элемент как возможную причину в случае неудачи. После перезапуска Docker я снова зашёл во вкладку Network в Resource Monitor и посмотрел, что привязано к порту 2375. Да, это Docker Desktop, всё хорошо, но он всё равно привязан только к Localhost! Даже при правильной конфигурации, описанной в документации, это приложение в Windows просто отказывается привязываться ко всем сетевым интерфейсам, становясь, по сути, бесполезным во всех случаях, за исключением самых тривиальных.

▍ Использование подсистемы Windows для Linux


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

Как же я ошибался.
Кто не в курсе, “WSL” означает “Windows Subsystem for Linux” и, возможно, является самым удивительным решением, какое разработчики из Misrosoft встраивали в свой флагманский продукт. Первая версия WSL добавляла хуки/оболочки непосредственно в ядро Windows, чтобы при выполнении скомпилированным бинарником Linux системных вызовов к тому, что он считает ядром Linux, ядро Windows принимало эти вызовы и перенаправляло или повторно реализовывало их логику «в стиле Windows», создавая для бинарника Linux иллюзию выполнения в родной среде. Представьте себе виртуализацию без необходимости использования гипервизора или установки другой ОС.

Как обычно, это решение оказалось не идеальным. Через какое-то время в Microsoft решили исправить ряд обнаруженных недочётов, выпустив WSL v2, чрезвычайно легковесную и полностью прозрачную виртуальную машину Windows, которая предоставляла всё те же возможности, но реализованные иначе. У второй версии были некоторые свои недочёты относительно первой, но на сегодня рекомендуемым выбором чаще является именно она.

Для использования WSL вам понадобится Windows 10, желательно последней сборки. При этом вам может потребоваться Pro Edition или более продвинутая версия (не уверен, поддерживается ли WSL в версии Home). Подробнее об установке этой оболочки читайте здесь.
Установка демона под Linux проблем не вызывает. Docker предоставляет прекрасную документацию для этого процесса и руководство по дальнейшим действиям. Обоим этим гайдам я следовал при установке Ubuntu 20.04 в WSL 2. Получилось довольно просто. Однако, поскольку запуск WSL несколько отличается от запуска типичного дистрибутива Linux, у вас не будет рабочего systemd, поэтому для автозапуска демона Docker потребуется найти другой способ.

Разобравшись с установкой и настройкой, вы сможете запускать демона Docker вручную выполнением dockerd от имени корневого пользователя.

Однако, если вы загляните во вкладку Network в Windows Resource Monitor, то dockerd там не увидите. Вместо этого он будет находиться под процессом wslhost, привязанном только к локальной петле. Что бы вы ни делали, WSL не позволит вам раскрыть находящиеся внутри него процессы напрямую для сети. То есть мы всё равно ограничены локальным loopback.

Так что для реализации задуманного нам потребуется дополнительная магия. Как насчет TCP-прокси?

▍ Установка прокси перед Docker: перебор безумных решений


Тогда стало понятно, что просто нет способа, который бы позволил мне раскрыть Docker для всей сети напрямую, поэтому я решил организовать перед ним TCP-прокси. Я рассуждал так: «Если docker CLI будет просто слепо передавать трафик туда-сюда, то никакой разницы не заметит».

Какой же тогда нужно организовать прокси? У меня был опыт работы с HAProxy, но у этого проекта нет версии для Windows. Мне же нужно было что-то, работающее в пользовательском пространстве Windows, так что этот вариант отпадал.

Единственным другим знакомым мне прокси-сервером был Envoy, поэтому я посетил их сайт в поиске доступной сборки для Windows. Оказалось, что «документация» предписывает для запуска Envoy в Windows компилировать его из исходного кода (так было на тот момент; надеюсь, в будущем это изменится), а у меня не было ни времени, ни терпения для установки среды разработки Windows только ради одной сборки Envoy. Эта идея казалась безумной.

Порывшись на их сайте, я нашёл соответствующий вопрос в FAQ, ведущий на эту страницу с исполняемыми файлами для скачивания. Но тут есть проблема: все файлы представлены в виде образов Docker. То есть мне для запуска Docker нужен Docker?

Полная бессмыслица. Тогда я решил заглянуть в релизы на GitHub – безуспешно. Затем я начал читать раздел “issues” проекта, надеясь, что кто-то уже обращался с вопросом о получении скомпилированного релиза для Windows.

К сожалению, самым полезным ответом на запрос о предоставлении скомпилированных бинарников Envoy для Windows оказался этот:
Смотрите раздел envoyproxy. Мы не предоставляем исполняемые файлы, но вы можете извлечь их из контейнера docker.
Эта тема продолжалась, и ниже были даны ссылки на документацию и инструмент для скачивания Envoy на машины Linux. Однако ни в одной документации не нашлось каких-либо упоминаний Windows (на момент написания статьи), что делало все эти ответы полностью бессмысленными.

Так что мне оставалось лишь идти и дербанить образ Docker.

▍ Нечестивые действия над образами Docker


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

Так что я решил вытащить официальный Envoy Docker Image for Windows из Docker Hub со своего Mac (используя Docker Desktop, который задействовал более 80% памяти, только чтобы достать образы):

$ docker pull envoyproxy/envoy-windows
Using default tag: latest
Error response from daemon: manifest for envoyproxy/envoy-windows:latest not found: manifest unknown: manifest unknown

Ну хорошо, Docker, пусть будет сложно. Попробуем просто прописать в качестве тега последнюю вышедшую версию:

$ docker pull envoyproxy/envoy-windows:v1.18.2
v1.18.2: Pulling from envoyproxy/envoy-windows
4612f6d0b889: Downloading
5ff1512f88ec: Downloading
ecaafce5c67e: Pulling fs layer
d507bef22a1e: Waiting
9bdb36426121: Waiting
e5b3f220cc68: Waiting
e5410c30e7a9: Waiting
4b47829da3b1: Waiting
35eb096aecb4: Waiting
68a2a4ec685d: Waiting
9c1c360fc8b3: Waiting
a08a596feca8: Waiting
image operating system "windows" cannot be used on this platform

И-и-и-и, облом. Docker не позволяет даже вытащить образ, если между операционными системами хоста и контейнера есть разночтения.

Разразившись рядом проклятий, я всё же решил не сдаваться. Я вспомнил, что образы Docker – это просто большие tar-архивы, поэтому у меня должно получиться скачать простой образ tar с Docker Hub и, теоретически, куда-нибудь его распаковать для просмотра файловой системы того, что, в ином случае, являлось бы образом Windows.

Оказалось, это не так уж просто. В извлечении образа Docker задействованы многие кастомные заголовки и токены, так что вручную делать это проблематично. К счастью, в ходе поисков мне попался скрипт из проекта Moby, который делал именно то, что было нужно. Я извлёк этот скрипт и выполнил его для получения образа Envoy под Windows:

$ ./script.sh win envoyproxy/envoy-windows:v1.18.2
Downloading 'envoyproxy/envoy-windows:v1.18.2@v1.18.2' (12 layers)...
<snip>
Download of images into 'win' complete.
Use something like the following to load the result into a Docker daemon:
  tar -cC 'win' . | docker load
Примечание: если вы получите ошибку, утверждающую ‘mapfile’ not found, возможно, нужно обновить версию Bash. MacOS по умолчанию поставляется со старой версией, отстающей на два старших релиза. Альтернативный вариант – выполнить этот скрипт на машине WSL, где может быть установлена более новая версия Bash.
Теперь можно просмотреть созданный скриптом каталог win/:

$ ls win
Permissions Size User Date Modified Name
drwxr-xr-x     - jah   4 May 15:42  1a5606bb70dc20fb56456b2583d295bdd2e847c0617da3da68d152bdd6a10b78
drwxr-xr-x     - jah   4 May 15:42  4e6cb5497aca4d83d2b91ef129fa823c225b0c76cefd88f5a96dd6c0fccdd6c7
drwxr-xr-x     - jah   4 May 15:42  6bfb8784732bcc28ef5c20996dbe6f15d3a004bf241ba59575b8af65de0a0aaf
drwxr-xr-x     - jah   4 May 15:42  3712aa599c08d0fb31285318af13e44d988391806d2460167643340c4f3a7123
drwxr-xr-x     - jah   4 May 15:42  698765937dc05ffcc458d8c2653563450bc169a724c62ed6a2c58f23c054b0ff
drwxr-xr-x     - jah   4 May 15:42  a4c3f3e7cef6cd7492338a26b7b307c0cd26e29379655f681d402c1eeaf595b6
drwxr-xr-x     - jah   4 May 15:42  b93d56fb00e644574bb7c2df769bb383d7fa351730393d46239078026bbc8efc
.rw-r--r--  3.7k jah   4 May 15:42  b775d72f61762e116864ab49adc8de32045e001efd1565c7ed3afe984d6e07f0.json
drwxr-xr-x     - jah   4 May 15:42  c42480d1b057b159309c4e55553ba75d84c21dc6c870f7ed77b0744c72e755f5
.rw-r--r--  3.7k jah   4 May 15:40  d00ba7ba582355550f5e42f453d99450754df890dec22fc86adb2520f3f81da2.json
drwxr-xr-x     - jah   4 May 15:42  d59df72a9d52b10ca049b2b6b1ce5b94f6ebb8a100ec71cea71ec7d8c0369383
drwxr-xr-x     - jah   4 May 15:43  d8067d34f431844ea7a3068d31cdb9254f1fcb93bcaf1c182ceebdec17c8d1fc
drwxr-xr-x     - jah   4 May 15:42  ea8955ac8603cc8dbb34e70e0922b59271522839db7d626e0f79f45b954c0d12
drwxr-xr-x     - jah   4 May 15:42  ec233e633fbbcbaf9d6f7ba3496ebc676f9b70ac4b95ba1127c466723976f55a
.rw-r--r--  1.2k jah   4 May 15:43  manifest.json
.rw-r--r--    51 jah   4 May 15:43  repositories


Повозившись немного с каждым каталогом и распаковав в каждом из них layer.tar, я, наконец, нашёл то, что искал:

$ cd 3712aa599c08d0fb31285318af13e44d988391806d2460167643340c4f3a7123
$ tar xf layer.tar && chmod -R 777 * && tree .
tree .
.
├── Files
│   ├── Documents\ and\ Settings -> [Error\ reading\ symbolic\ link\ information]
│   └── Program\ Files
│       └── envoy
│           └── envoy.exe
├── Hives
│   ├── DefaultUser_Delta
│   ├── Sam_Delta
│   ├── Security_Delta
│   ├── Software_Delta
│   └── System_Delta
├── VERSION
├── json
└── layer.tar
4 directories, 10 files

Вот оно! Мы нашли envoy.exe!

Скопировав envoy.exe из контейнера архива tar, теперь я мог написать файл конфигурации для проксирования TCP-трафика с 0.0.0.0:2375 в локальный loopback на одном порту. Конфигурация получилась такой:

static_resources:
  listeners:
    - name: docker-proxy
      address:
        socket_address:
          address: 0.0.0.0
          port_value: 2375
          protocol: TCP
      filter_chains:
        filters:
          - name: envoy.filters.network.tcp_proxy
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy
              cluster: docker-actual
              stat_prefix: docker-proxy
  clusters:
    - name: docker-actual
      connect_timeout: 1s
      type: STATIC
      load_assignment:
        cluster_name: docker-actual
        endpoints:
          - lb_endpoints:
            - endpoint:
                address:
                  socket_address:
                    address: 172.25.65.236
                    port_value: 2375
                    protocol: TCP
admin:
  address:
    socket_address:
      address: 0.0.0.0
      port_value: 9901

Важно: при каждом запуске WSL присваивает себе новый адрес IPv4. Я пока не выяснил, как заставить её всегда использовать одинаковый адрес. Так что вам потребуется получать IP-адрес WSL через выполнение ifconfig eth0 из оболочки WSL и подставлять свой вместо того, что приведён в моём примере конфигурации. Это нужно будет делать каждый раз при перезагрузке машины или остановке и старте WSL.

▍ Запускаем


Подготовив Envoy, можно всё запускать и приступать к тестированию. На своей машине с Windows я открыл в WSL оболочку, откуда вручную запустил Docker:

$ sudo dockerd --tls=false &

После в другом окне терминала я запустил сеанс Powershell и Envoy:

PS J:\envoy> .\envoy.exe -c .\envoy.yaml

Если всё сработает должным образом, то вы увидите, что wslhost привязан к локальной петле на порту 2375, а envoy.exe привязан к “IPv4 unspecified” также на порту 2375.


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

$ export DOCKER_HOST=<ip of windows box>
$ docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

Работает!

▍ Выводы


На всякий случай повторюсь, что всё проделанное мной никогда не следует делать в продакшене. Это был мой отдельный случай в собственной сети. Меня интересовало исключительно удобство разработки и тестирования.

Описанная здесь настройка требует немало ручной работы. При каждой перезагрузке хоста вам потребуется выяснять внутренний IPv4 адрес для WSL, изменять эту запись в конфигурации Envoy, запускать dockerd в WSL и снова запускать envoy.exe для подключения. Сейчас я делаю всё это сам, так как машину с Windows перезагружать мне приходится нечасто. И всё же я хочу найти способ автоматизировать выполнение всего этого процесса при загрузке компьютера. Думаю, что это можно реализовать с помощью файла .BAT, вызываемого через “Запланированную задачу” (см. «Планировщик задач») при каждом запуске компьютера до стадии авторизации.

Весь проделанный путь оказался сложнее, чем должен был. Здесь мы столкнулись и с отказом Windows Docker Desktop признавать конфигурацию host, и с заявлением проекта Envoy о том, что у них есть релиз под Windows, который оказалось столь сложно извлечь, а также с тем, что Microsoft по умолчанию не позволяет раскрывать для сети процессы в WSL, привязанные к портам. Есть объективный повод критиковать все три причастные компании за излишнее усложнение вещей, которые должны быть простыми. При этом также нужно признать, что всё проделанное мной оказалось бы невозможным, если бы не прекрасная работа, которую люди из вышеупомянутых компаний проделали для создания превосходных инструментов, позволяющих таким как я реализовывать подобные «безумные» приёмы.

Однако признаю, что удалять Docker Desktop из Windows было необязательно, поскольку установленный в WSL демон всё равно оказался привязан только к локальной петле. Теоретически должна быть возможность проделать всё это без использования WSL (минус извлечение образа Envoy, для чего необходима последняя версия Bash). Но ключевое слово здесь «теоретически» — я не пробовал сделать это с помощью Docker Desktop для Windows и понятия не имею, какие ещё проблемы могут возникнуть, если пойти этим путём.

Закончу тем, что всего этого можно было избежать, если бы:

  • Apple ставили в свои MacBook Pro больше 16ГБ ОЗУ или;
  • Docker не требовал целую виртуальную машину с выделенными ресурсами для «имитации» контейнеров в MacOS.

Но мы ведь живём не в идеальном мире.
Telegram-канал с полезностями и уютный чат
Tags:
Hubs:
Total votes 24: ↑22 and ↓2+34
Comments12

Articles

Information

Website
ruvds.com
Registered
Founded
Employees
11–30 employees
Location
Россия
Representative
ruvds