Pra poder acessar o Docker a partir do Windows Subsystem for Linux (WSL) eu costumava abrir a porta 2375 no Docker host, e configurava no meu .bashrc
a variável de ambiente DOCKER_HOST
para o valor :2375
. Isso resolvia o problema, mas deixava meu sistema exposto externamente (ainda que o firewall do Windows bloqueasse o acesso, me incomodava).
Agora que, na versão 1803 do Windows (que está pra sair, ou já saiu, quando eu escrevi esse post estava quase), é possível utilizar sockets Unix no Windows eu poderia desligar isso, e utilizar o socket padrão do Docker. É exatamente a proposta deste post do blog de Command Line do Windows. Mas ele só resolve metade do problema, porque você precisa, toda vez que abrir o bash do WSL, rodar um comando pra habilitar o encaminhamento do socket. E pior, como root. E se algo der errado o arquivo de socket fica lá morto e você precisa arrumar na mão. Vamos resolver esses detalhes.
Resolvi isso mudando o socket de lugar. Então, pra resumir, vou explicar desde o começo pra quem quiser experimentar.
Instale o Go
Instalar o Go é basicamente copiar alguns arquivos. Siga o script abaixo:
#atualize suas fontes sudo apt-get update #baixe a última versão sudo wget https://dl.google.com/go/go1.10.1.linux-amd64.tar.gz #dezipe sudo tar -C /usr/local -xzf go1.10.1.linux-amd64.tar.gz #Coloque os binários no path export PATH=$PATH:/usr/local/go/bin
Inclua a última linha do script acima também no seu script .bashrc
(ou o seu script de startup, caso você use zsh ou outros), assim no próximo início do shell você continuará tendo o Go no PATH.
Fazendo o build do npiperelay
Em seguida vamos buildar e instalar o npiperelay, que permite acessar named pipes do Windows a partir do WSL. O binário final do npiperelay pode ficar em qualquer lugar, desde que seja do lado Windows do sistema de arquivos. Não tente colocar do lado WSL das coisas, ou o WSL vai tentar executá-lo direto, e não funcionar. Esse binário é um binário Windows, e será executado sem intermédio do WSL, ele não é um binário do Linux. Note que você precisará substituir o seu nome de usuário nos scripts a seguir:
go get -d github.com/jstarks/npiperelay GOOS=windows go build -o /mnt/c/Users/<seu-usuario-no-windows>/go/bin/npiperelay.exe github.com/jstarks/npiperelay
Instalando socat e docker
Em seguida, instale o socat que vai encaminhar as chamadas do socket do Linux pro Windows.
sudo apt install socat -y
E vamos instalar também também o Docker no WSL, seguindo as instruções de Ubuntu do site do Docker:
curl -fsSL get.docker.com -o get-docker.sh sudo sh get-docker.sh
Não se esqueça de adicionar seu usuário no grupo do docker:
sudo usermod -aG docker seu-usuario-no-linux
Preparando o relay
Em seguida, crie o arquivo docker-relay
em algum lugar onde você possa executar binários. Eu coloquei no meu diretório ~/bin
porque ele está no PATH
. Outra opção é o no /usr/local/bin
, que costuma já estar no PATH
:
#!/bin/sh SOCKET=/home/seu-usuario-no-linux/sockets/docker.sock #remove o arquivo de socket se ele não foi devidamente excluído da última vez if [ -e $SOCKET ]; then rm $SOCKET; fi exec socat UNIX-LISTEN:$SOCKET,fork,group=docker,umask=007 EXEC:"/mnt/c/Users/seu-usuario-no-windows/go/bin/npiperelay.exe -ep -s //./pipe/docker_engine",nofork
Cuidado com as alterações de usuário, já que são usados os usuários do Linux e do Windows em lugares diferentes.
Você precisa dar permissão de execução ao arquivo docker-relay
, faça isso com:
chmod +x docker-relay
Configure o Docker host:
export DOCKER_HOST="unix://$HOME/sockets/docker.sock"
Não deixe de criar o diretório pro socket que é mencionado no arquivo acima:
mkdir ~/sockets
Testando se funcionou
Saia e entre novamente no bash pra garantir que o grupo do Docker está ativo. Então, rode:
export DOCKER_HOST="unix://$HOME/sockets/docker.sock"
docker-relay
Garanta que o Docker está rodando: abra outra janela do WSL e digite:
docker version
O resultado deve ser parecido com o abaixo. No meu caso estava rodando com contêineres Windows, então note que o client é Linux (o WSL), mas o server é Windows:
Client: Version: 18.03.0-ce API version: 1.37 Go version: go1.9.4 Git commit: 0520e24 Built: Wed Mar 21 23:10:01 2018 OS/Arch: linux/amd64 Experimental: false Orchestrator: swarm Server: Engine: Version: 18.03.0-ce API version: 1.37 (minimum version 1.24) Go version: go1.9.4 Git commit: 0520e24 Built: Wed Mar 21 23:21:06 2018 OS/Arch: windows/amd64 Experimental: true
Pra garantir que está ok, rode ps aux | grep socat
, e você verá o socat
rodando, iniciado pelo nosso script docker-relay
anterior.
Rodando automaticamente
O chato é que pra isso funcionar você precisaria, toda vez que iniciar o WSL, rodar docker-relay &
(o &
é pra desatachar o terminal e deixar em segundo plano). Além disso, ao sair do WSL o processo do socat
morre, ou seja, toda vez que você inicia um novo WSL você teria que iniciar o socat
e isso leva tempo.
Pra resolver isso, vamos iniciar uma sessão de tmux colocando somente o socat
rodando lá. O Tmux, ou “Terminal Multiplexer” permite rodar outros terminais dentro da mesma janela, estando anexados na janela atual ou não. O Tmux é muito legal por vários motivos, e aqui ele será útil por esse. Aprenda Tmux depois, se você já não conhece.
Colocaremos o socat
em uma sessão do Tmux, e, ao sair do WSL, o socat
continuará rodando.
Coloque nos seus scripts de inicialização (novamente, seu .bashrc
ou arquivo preferido) o seguinte script:
if cat /proc/version | grep Microsoft > /dev/null; then export WSL=true fi if [ "$WSL" ]; then export DOCKER_HOST="unix://$HOME/sockets/docker.sock" if ! pgrep socat > /dev/null; then tmux new -s docker-relay-session -d docker-relay fi fi
Primeiro verificamos se estamos no WSL. Eu faço isso porque compartilho meus arquivos entre Linux com kernel nativo e o WSL. Tenho um repositório no github em giggio/bashscripts caso você queira ver o que estou usando. Assim, caso o script rode direto no Linux, não preciso tentar encaminhar o socket.
Em seguida, exponho o host do docker no novo socket, que está na minha home, no diretório criado anteriormente. E então verifico se o socat
está rodando, com pgrep
. Se estiver, eu não tento rodar ele novamente.
E se for rodar, rodamos com Tmux.
Com tudo isso no lugar, feche todas as janelas do Bash, e abra uma novamente. Tente rodar um docker version
e confirme se funciona. Rode um ps aux | grep socat
e confirme que ele está rodando também, e anote o número do processo. Saia do WSL, e abra o task manager, e clique More details no canto inferior esquerdo. O Task Manager é capaz de exibir os processos do WSL (do Linux) rodando. Procure pelo socat
, ele deve estar lá. Abra novamente o WSL e rode novamente o ps aux | grep socat
, confirme que o número do processo do socat
é o mesmo de antes.
É isso. Dessa forma, o seu Docker fica seguro, sem a porta 2375 exposta, e o acesso ao Docker continua funcionando.
Analisei a demanda de memória, e socat ocupa pouco mais de 1.5kb e o tmux um pouco menos. No total, dá 3kb, ou seja, o impacto é irrisório.
Bônus: rodando contêineres Linux sem VM
Pra terminar bonito, coloque o Docker em modo de contêineres Windows e rode o seguinte comando:
docker run --rm -ti --platform=linux alpine
Agora pra inception: você está dentro de um terminal do Alpine Linux, através de um socket Unix encaminhado pra um named pipe do Windows, falando com um host Windows do Docker que está rodando um contêiner Linux sem máquina virtual (veja aqui como isso é possível).
Bônus 2: rodando Windows e compartilhando volumes
Agora rode:
docker run --rm -ti -v c:\\windows:c:\\windowshost microsoft/windowsservercore
Dentro do contêiner, rode dir \windowshost
pra ver os arquivos do host.
Nesse caso você está rodando um contêiner Windows, e compartilhando um volume que aponta pro diretório c:\windows
.
Não é possível compartilhar diretórios do WSL, você precisará utilizar um diretório que esteja visível pro Windows. E nunca utilize o /mnt/c/
, ele não vai funcionar.
É isso, dá um pouquinho de trabalho, mas o resultado fica bom. Você curtiu? O que achou desse hack todo?
Giovanni Bassi
Arquiteto e desenvolvedor, agilista, escalador, provocador. É fundador e CSA da Lambda3. Programa porque gosta. Acredita que pessoas autogerenciadas funcionam melhor e por acreditar que heterarquia é mais eficiente que hierarquia. Foi reconhecido Microsoft MVP há mais de dez anos, dos mais de vinte que atua no mercado. Já palestrou sobre .NET, Rust, microsserviços, JavaScript, TypeScript, Ruby, Node.js, Frontend e Backend, Agile, etc, no Brasil, e no exterior. Liderou grupos de usuários em assuntos como arquitetura de software, Docker, e .NET.