【问题标题】:Dockerfile HOSTNAME Instruction for docker build like docker run -hDockerfile HOSTNAME 用于 docker build 的指令,如 docker run -h
【发布时间】:2015-05-23 07:12:54
【问题描述】:

我试图在构建过程中在 docker 容器中设置主机名,因为某些软件安装使用发现的随机生成的主机名并将该主机名永久地烘焙到配置中。

虽然可以通过 run -h 交互式运行时设置主机名,但通过 Dockerfile 构建时无法使用相同的功能。

解决此问题的唯一方法是使用 LD_PRELOAD hack,以便我可以将主机名设置为 localhost。 LD_PRELOAD hack 有一些不想要的副作用,我无法解决这些副作用。使用“docker run -it -h localhost”时,软件安装没有问题。

strace 报告安装程序调用 uname 确定主机名。

uname({sys="Linux", node="12b0c7c7eacb", ...}) = 0

有谁知道如何解决这个限制?

更新 1

这不是问题How to handle specific hostname like -h option in Dockerfile 的重复,因为它专门讨论了动态生成的文件引起的“/etc/hosts”问题。这很容易解决,因为它是一个可写文件。

这是关于尝试从系统调用(例如 uname 和 gethostname)解析主机名的软件安装。据我所知,这是无法解决的,因为无法在正在运行的 docker 容器中更改主机名。 uname 系统调用可能引用 /proc/sys/kernel/hostname,这是只读的,不能更改。通常可以运行 hostname 命令,但是该命令会生成一个错误,即即使您是 root,您也必须是 root。解决方法是使用 -h 标志,这在构建中不可用。

更新 2

对于任何在这里寻找解决方法的人来说,这只需要在 docker 构建期间使用,如果您需要使用 docker run 自定义主机名,请使用 -h 标志。这是基于其他人的工作。

Dockerfile:

RUN gcc -o fakehostname.o -c -fPIC -Wall fakehostname.c
RUN gcc -o libfakehostname.so -shared -W1,export-dynamic fakehostname.o -ldl

RUN ..
     export LD_PRELOAD=/u01/app/oracle/libfakehostname.so;\
     installer section
    ..

C 来源:

#define _GNU_SOURCE
#include <unistd.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/utsname.h>

#include <stdio.h>
#include <string.h>

static int (*real_gethostname)(char *name, size_t len);

int uname(struct utsname *buf)
{
 int ret;

 ret = syscall(SYS_uname, buf);

 strcpy(buf->nodename, "localhost");

 return ret;
}

int gethostname(char *name, size_t len)
{
  const char *val;

  /* Override hostname */
  val = "localhost";
  if (val != NULL)
  {
    strncpy(name, val, len);
    return 0;
  }

  /* Call real gethostname() */
  return real_gethostname(name, len);
}

http://github.com/docker/docker/issues 根据 into_the_void,因为没有解决此问题的方法。

【问题讨论】:

标签: docker dockerfile


【解决方案1】:

我最近遇到了类似的问题。

对我有用的解决方案是在容器命名空间中设置主机名。 为此,我将以下 docker build 脚本放在一起:

docker build . | tee >((grep --line-buffered -Po '(?<=^change-hostname ).*' || true) | while IFS= read -r id; do nsenter --target "$(docker inspect -f '{{ .State.Pid }}' "$id")" --uts hostname 'new-hostname'; done)

可以将末尾的new-hostname 替换为所需的主机名。

我的 Dockerfile 看起来像这样:

RUN echo "change-hostname $(hostname)"; \
    sleep 1; \
    printf '%s\n' "$(hostname)" > /etc/hostname; \
    printf '%s\t%s\t%s\n' "$(perl -C -0pe 's/([\s\S]*)\t.*$/$1/m' /etc/hosts)" "$(hostname)" > /etc/hosts; \
    echo 'Installing more stuff...'

打印出change-hostname $(hostname)(主机名应该打印出当前容器ID)的第一行表示构建脚本更改该容器的主机名。然后构建脚本查询容器的 pid 并在其 uts 命名空间中执行 hostname 'new-hostname'sleep 1 只是给构建脚本一些时间来正确调整主机名。然后我修改/etc/hostname/etc/hosts 以包含新设置的主机名。

这甚至改变了uname -n 的输出,所以我很确定它可以作为原始问题的解决方案。

【讨论】:

    【解决方案2】:

    让我看看我是否理解您的问题,您想构建一个在作为容器运行时具有运行时主机名的映像,即使用于构建的主机名不同。正确的?如果是这样,我向您提出的问题如下,您能否在安装软件后重新配置软件以使用新主机名?

    如果可能,我建议编写一个能够修改主机名的脚本并将该脚本用作ENTRYPOINT。这样,您可以保证在容器运行时(使用任何命令)都已更正主机名,并且您不会花时间尝试在构建时强制支持特定的主机名,您自己承认,这很困难去做。

    【讨论】:

    • 这是一个很好的建议,入口点脚本是我们为所有 docker 镜像所做的。不幸的是,该软件在大约 60 个位置配置主机名,包括文本文件、xml 文件和内部二进制文件。推荐的主机名更改方法意味着重新安装软件以及一些额外的步骤。这意味着入口点启动时间将从 2 分钟增加到大约 40 分钟,这是不可行的。
    • 好吧,是时候试试问题跟踪器 (github.com/docker/docker/issues),因为我不相信这个功能存在,或者至少没有遇到过。 – into_the_void 55 分钟前
    • 感谢您提供此信息,我认为这是正确答案。
    【解决方案3】:

    您可以使用docker-compose 来构建您的映像并分配主机名,例如

    version: '3'
    services:
      all:
        image: testimage
        container_name: myname
        hostname: myhost
        build:
          context: .
    

    运行为:docker-compose --build up

    【讨论】:

    • docker compose 是在构建步骤中实际使用指定的主机名,还是仅在运行步骤中使用?我很想看到一个简单的构建文件的演示,它在构建时和运行时输出主机名。
    • 这仅在运行时设置主机名。不是在构建期间。至少在 Mac 上使用“docker-compose version 1.23.2, build 1110ad01”。
    猜你喜欢
    • 2016-06-16
    • 1970-01-01
    • 2021-05-02
    • 1970-01-01
    • 2016-05-30
    • 1970-01-01
    • 2018-02-23
    • 1970-01-01
    • 2020-08-10
    相关资源
    最近更新 更多