前言

前言

前言

我想编写一个通用的 shell 脚本,它将 docker 主机的正确 IP 地址添加到/etc/hosts容器内部。

你可能会建议我将我想要访问的服务也放在另一个 docker 容器中,但是为了对 CI 进行最快的测试,我想通过容器的 ip 访问 docker 主机,这就是我需要它的原因。

有一个github上的问题有很多评论人们要求制定统一的方式来访问主机IP。没有适用于所有平台 ATM 的通用解决方案。

人们制作这样的脚本:

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

为了访问 docker 主机,我想使用相同的方法,但它不适用于 Mac atm。返回的结果为:

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

如果 Docker 主机是 Mac ,则会给出错误的答案172.17.0.1(您实际上无法使用 容器从容器访问您的 Mac 172.17.0.1)。

这就是为什么 Docker for Mac 中有一个功能 -特殊主机名 docker.for.mac.localhost您可以使用它从容器访问您的主机 Mac。

问题

我想编写一个通用的shell 脚本,它首先尝试解析docker.for.mac.localhost并将解析的地址添加到/etc/hosts.如果没有(这意味着 docker 主机是 windows 或 linux,而不是 Mac),请使用该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,并且可以通过更简单的方式检索它。请分享您的想法。

谢谢。

答案1

您解决 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) 的一部分或某种初始化脚本中。

如需更多信息、更完整的评论版本以及注意事项,请继续阅读。

我们所做的就是让 Linux 解析名称。尽管 nc 和traceroute 几乎无处不在,但 Ping 只是最简单的选择。如果所有其他方法都失败了,您实际上可以使用现代(但非 BusyBox)bash 或 syslog 来完成此操作。默认情况下,Docker 设置一个小型 DNS 服务器,用于响应同一网络上任何其他容器(或者在旧版本中,链接容器)的名称。然后docker.for.mac.localhost添加到其中。

如果没有任何解析,则说明该 dns 服务器未解析它,我们可以正常获取主机。您可能希望改用 /proc/net 方法,但它们在我的测试容器中似乎是等效的。

现在,有一些注意事项。首先,一个大问题:如果您没有网络连接,这将永远超时 - 最多 30 秒。下面的较大脚本中有一些修复。您也不能(默认情况下 - 有方法可以覆盖它)在容器的构建过程中运行它。相反,你可以,它只是不会做任何事情:当你启动容器时,Docker 会挂载一个新的 /etc/hosts。

更好的版本会使用timeout,但是你遇到了一个问题 - 它(至少)有 2 个不兼容的版本,如果你想假设它可能不存在,还有第三种情况:

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

我们使用 env 是因为几乎可以肯定有一个版本不是内置的 shell(/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

我相信有提到这里可以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 }'

相关内容