web-dev-qa-db-ja.com

"docker.for.mac.localhost" DNS名が解決可能かどうかを確認する普遍的な方法は?

序文

コンテナー内の_/etc/hosts_にdocker Hostマシンの適切なIPアドレスを追加するユニバーサルシェルスクリプトを記述したいと思います。

アクセスしたいサービスを別のDockerコンテナーに入れることをお勧めしますが、CIで最も高速なテストを行うには、コンテナーからIPでDockerホストマシンにアクセスしたいので、それが必要です。

githubにたくさんのコメントがある問題 があり、人々はホストIPにアクセスするための統一された方法を作ることを求めていました。すべてのプラットフォームatmに共通の解決策はありません。

人々はこのようなスクリプトを作成します:

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

Docker Hostマシンにアクセスするために、同じアプローチを使用したいのですが、Mac atmでは機能しません。返される結果:

_ip r | grep ^default | cut -d" " -f3_

docker HostマシンがMacの場合、_172.17.0.1_は間違った答えを返します(_172.17.0.1_を使用してコンテナーからMacに実際にアクセスすることはできません)。

Docker for Macに機能があるのはそのためです- 特別なホスト名 _docker.for.mac.localhost_を使用して、コンテナーからホストMacにアクセスできます。

問題

最初に_docker.for.mac.localhost_を解決し、解決されたアドレスを_/etc/hosts_に追加するユニバーサルshellスクリプトを記述したいと思います。そうでない場合(つまり、Docker HostがMacではなくWindowsまたはLinuxのいずれかであることを意味します)、_ip r | grep ^default | cut -d" " -f3_アプローチを使用し、_/etc/hosts_にも追加します。

問題は、どのコンテナで_docker.for.mac.localhost_を解決しようとすることができますか? (たとえば、一部のディストリビューションにはnslookupがありません)。または、解決しようとするべきではなく、どこかから_docker.for.mac.localhost_値を読み取ろうとするだけですか?

私の現在の考えは、_getent ahosts docker.for.mac.localhost_を使用することです。コマンドが存在しない場合は、nslookupを使用しようとします(つまり、Busyboxイメージにはgetentコマンドがありません)。おそらく私は_docker.for.mac.localhost_を解決しようとして間違っており、簡単に取得できます。あなたの考えを共有してください。

ありがとうございました。

4
Kirill

Docker.for.mac.localhostを解決するというあなたの考えは、目標どおりにかなりうまくいきます。ほとんどすべてのコンテナーでそれを解決できる何かを見つける必要があるだけです。これはそれを行うはずです(そして私のテストによれば、持っています):

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

それをエントリポイントの一部として置くことができます(sh -c)または何らかのinitスクリプトで。

詳細、コメント付きバージョン、および警告については、以下をお読みください。

Linuxに名前を解決させるだけです。 pingは最も簡単な選択ですが、ncとtracerouteはほとんどどこにでも存在します。他のすべてが失敗した場合は、最新の(BusyBox以外の)bashまたはsyslogで実際にこれを行うことができます。デフォルトでは、Dockerは同じネットワーク上の他のコンテナー(または、以前のバージョンではリンクされたコンテナー)の名前に応答する小さなDNSサーバーをセットアップします。その後 docker.for.mac.localhostが追加されます。

何も解決しない場合、そのDNSサーバーはそれを解決しなかったため、ホストは通常​​どおり取得されます。代わりに/ proc/netメソッドを使用したいと思うかもしれませんが、それらは私のテストコンテナーでは同等のようです。

ここで、いくつかの注意点があります。まず、大きな問題です。ネットワーク接続がない場合、タイムアウトするまでに最大30秒かかります。以下の大きなスクリプトにいくつかの修正があります。また、コンテナーのビルド中にこれを実行することもできません(デフォルトでは、これをオーバーライドする方法があります)。むしろ、可能ですが、何も実行しません。Dockerはコンテナーを起動すると、新しい/ etc/hostsをマウントします。

より優れたバージョンではtimeoutを使用しますが、問題が発生します-(少なくとも)2つの互換性のないバージョンがあり、3つ目のケースはそれが存在しない可能性があると想定したい場合です。

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

シェル組み込みではないバージョン(/usr/bin/envほとんどの場合)、それはよく知られているインターフェイス(つまり、入力なしで実行されます)を備えており、ファイルシステムに依存するlsなどの厄介なコマンドとは異なり、環境上の理由で失敗することはほとんどありません。

そして最後に、私が使用しているものと同様の完全なコメント付きバージョン(私の後ろに来る人々が何が起こっているのかを理解できるように):

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"
2
TheDauthi

私はそれが言及されていると思います ここdocker.for.mac.Host.internalが使用できること。 docker.for.mac.localhostについては言及されていませんが、私にとっても機能します。

次に、次の行を使用して、Docker for Macで実行しているかどうかを確認します。

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

$?は、docker.for.mac.Host.internalが見つかったかどうかを通知します。

実際のIPを取得するには、次のようにawkを使用することもできます。

$ getent hosts docker.for.mac.Host.internal | awk '{ print $1 }'
1
JonasVautherin