Maneira universal de verificar se o nome DNS “docker.for.mac.localhost” é solucionável?

4

Prefácio

Eu quero escrever um script de shell universal que irá adicionar um endereço IP adequado da máquina host do docker a /etc/hosts dentro do container.

Você provavelmente vai me aconselhar a colocar o serviço que eu quero acessar em outro container docker também, mas para testes mais rápidos no CI eu quero acessar a máquina host do docker por ip do container, é por isso que eu preciso.

Houve um problema no github com muitos comentários em que as pessoas pediam para fazer um caminho unificado para acessar o host ip. Não existe uma solução comum para todas as plataformas atm.

As pessoas criam scripts como este:

grep dockerhost /etc/hosts || echo $(ip r | grep ^default | cut -d" " -f3) dockerhost >> /etc/hosts

Para acessar a máquina host do docker, eu gostaria de usar a mesma abordagem, mas ela não funciona para o Mac atm. O resultado retornado por:

ip r | grep ^default | cut -d" " -f3

dará uma resposta errada 172.17.0.1 se a máquina host do Docker for Mac (você não poderá acessar o Mac do container com 172.17.0.1 ).

É por isso que há um recurso no Docker para Mac - nome de host especial docker.for.mac.localhost com o qual você pode acessar seu host Mac a partir do container.

O problema

Gostaria de escrever um script universal sh ell que tentará primeiro resolver docker.for.mac.localhost e adicionar o endereço resolvido a /etc/hosts . Se não (o que significa que o host do docker é o Windows ou o Linux, não o Mac), use a abordagem ip r | grep ^default | cut -d" " -f3 e adicione a /etc/hosts também.

A pergunta é: Como posso tentar resolver docker.for.mac.localhost em QUALQUER contêiner? (algumas distros não possuem nslookup , por exemplo). Ou talvez eu não deva tentar resolver e posso apenas tentar ler docker.for.mac.localhost em algum lugar?

Minhas idéias atuais são tentar usar getent ahosts docker.for.mac.localhost , então, caso o comando não exista, tentarei usar nslookup (ou seja, a imagem do Busybox não tem o comando getent ). Provavelmente estou errado ao tentar resolver docker.for.mac.localhost e ele pode ser recuperado de maneira mais fácil. Por favor, compartilhe seus pensamentos.

Obrigado.

    
por Derp 18.09.2017 / 18:23

2 respostas

2

Sua idéia de resolver o docker.for.mac.localhost é muito bem-alvo, você só precisa encontrar algo capaz de resolvê-lo em praticamente qualquer contêiner. Isso deve ser feito (e tem, de acordo com meus testes):

grep dockerhost /etc/hosts || echo $( (ping -c1 docker.for.mac.localhost || (ip r | grep ^default)) | grep -oE '([0-9]+\.){3}[0-9]+' ) dockerhost >> /etc/hosts

Você pode colocar isso como parte de seu entrypoint ( sh -c ) ou em algum script de init.

Para mais informações, uma versão comentada mais completa e as advertências, continue a ler.

Tudo o que estamos fazendo é fazer com que o Linux resolva o nome. O ping é a escolha mais fácil, embora o nc e o traceroute existam em praticamente todos os lugares. Se tudo mais falhar, você pode realmente fazer isso com um moderno (mas não BusyBox) bash ou syslog. Por padrão, o Docker configura um pequeno servidor DNS que responde aos nomes de quaisquer outros contêineres na mesma rede (ou, em versões mais antigas, contêineres vinculados). E então docker.for.mac.localhost é adicionado a isso.

Se nada resolver, então o servidor dns não resolveu, e nós recebemos o host normalmente. Você pode querer usar o método / proc / net, mas eles parecem ser equivalentes em meus contêineres de teste.

Agora, algumas ressalvas. Primeiro, um grande problema: se você não tiver conexão de rede, isso levará uma eternidade para o tempo limite - até 30 segundos. Existem algumas correções no script maior abaixo. Você também não pode (bem, por padrão - existem maneiras de sobrescrever isso) executar isso durante a compilação do contêiner. Em vez disso, você pode, simplesmente não fará nada: o Docker monta um novo / etc / hosts quando você inicia o container.

Uma versão melhor usaria timeout , mas você se depara com um problema - há (pelo menos) duas versões incompatíveis e um terceiro caso, se você quiser supor que pode não estar lá:

grep dockerhost /etc/hosts || echo $( (
(if timeout -t1 env >/dev/null; then \
     timeout -t1 ping -c1 docker.for.mac.localhost; \
   elif timeout 1s env >/dev/null; then
      timeout 1s ping -c1 docker.for.mac.localhost; \
   else \
      ping -c1 docker.for.mac.localhost; \
fi) \
|| (ip r | grep ^default)) \
| grep -oE '([0-9]+\.){3}[0-9]+' ) dockerhost >> /etc/hosts

Usamos env porque quase certamente existe uma versão que não é integrada ao shell ( /usr/bin/env na maioria dos casos), tem uma interface bem conhecida (ou seja, será executada sem nenhuma entrada) e é improvável que ele falhe por motivos ambientais, ao contrário de comandos irritantes como ls , que dependem de um sistema de arquivos.

E, finalmente, uma versão completa comentada semelhante ao que estou usando (para que as pessoas que vêm atrás de mim entendam o que está acontecendo):

add_dockerhost_address() {
  # The name we're checking for - in case this technique is useful on Windows 
  # if we get a Windows version instead of a cross-platform one
  local MACHOST="docker.for.mac.localhost"
  # The name we want to insert into /etc/hosts
  local DOCKERHOSTNAME="dockerhost"

  # Don't insert the name twice
  if grep "$DOCKERHOSTNAME" /etc/hosts >/dev/null 2>&1; then
    return 0
  fi

  # A command string we'll be building
  local COMMAND=""

  # Check for the ability to limit the timeout
  if command -v timeout >/dev/null 2>&1; then
    # We still have a problem - there are two widely used incompatible
    # variants of timeout. Let's see which one we have.

    # env should not be a builtin, doesn't require a pipe,
    # has a consistent command line test, and is unlikely to fail
    if timeout -t1 env >/dev/null 2>&1; then
      COMMAND="timeout -t1 "
    else
      COMMAND="timeout 1s "
    fi
  fi

  local IS_NOT_MAC=1
  local IP=""

  if command -v ping >/dev/null 2>&1; then
    COMMAND="$COMMAND ping -c1 $MACHOST"
    # Otherwise, BusyBox shows terminated.
    IP=$(sh -c "$COMMAND" 2>&1)
    # Exit codes: 0 - found the host, 1/2 failed to find, 124/143 terminated
    IS_NOT_MAC="$?"
  fi

  if [ "$IS_NOT_MAC" -eq 0 ]; then
    IP=$(echo "$IP" | grep -oE '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}')    
  else
    IP=$(ip r | grep ^default | cut -d" " -f3)
  fi

  echo "$IP $DOCKERHOSTNAME" >> /etc/hosts
}

add_dockerhost_address

exec "/usr/sbin/apachectl" "-DFOREGROUND"
    
por 02.01.2018 / 06:10
1

Acredito que seja mencionado aqui que docker.for.mac.host.internal pode ser usado. Não há menção de docker.for.mac.localhost , embora também funcione para mim.

Depois, uso a seguinte linha para verificar se estou executando no Docker para Mac ou não:

$ getent hosts docker.for.mac.host.internal

$? informa se docker.for.mac.host.internal foi encontrado.

Para obter o IP real, também é possível usar awk , assim:

$ getent hosts docker.for.mac.host.internal | awk '{ print $1 }'
    
por 14.03.2018 / 15:58