Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dockerfile HOSTNAME Instruction for docker build like docker run -h

I am attempting to set the hostname inside a docker container during the build since certain software installs use the discovered randomly generated hostname and permanently bake that hostname into the configuration.

While it is possible to set the hostname when you run interactively via run -h, the same functionality is not available using build via the Dockerfile.

The only way to work around this is to use LD_PRELOAD hacks so that I can set the hostname to localhost. The LD_PRELOAD hacks have unwanted sideeffects that I am having trouble working around. The software install works without issue when using "docker run -it -h localhost ".

strace reports that the installer make a call to uname determine the hostname.

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

Does anyone know how it might be possible to work around this limitation?

Update 1

This is not a duplicate of the question How to handle specific hostname like -h option in Dockerfile as that is talking specifically about "/etc/hosts" problems arising from that file being dynamically generated. This is easily worked around since it is a writable file.

This is about software installs that attempt to resolve the hostname from system calls such as uname and gethostname. From what I can tell this cannot be worked around since the hostname cannot be changed within a running docker container. The uname system call likely references /proc/sys/kernel/hostname, this is read only and cannot be changed. Normally the hostname command could be run, but this command generates an error that you must be root even if you are root. The workaround for this is to use the -h flag, this is not available in builds.

Update 2

For anyone looking for a workaround here it is, this only needs to be used during the docker build, use the -h flag if you need to customize the hostname with docker run. This is based on someone else's work.

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 Source:

#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 as per into_the_void as there is no solution to this problem.

like image 489
Trevor Fuson Avatar asked Mar 19 '15 23:03

Trevor Fuson


3 Answers

I have recently had a similar issue.

The solution that worked for me was to set the hostname in the container namespace. To do that automatically I have put together the following docker build script:

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)

Right at the end new-hostname can be replaced with the desired hostname.

My Dockerfile looks like this:

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...'

The first line that prints out change-hostname $(hostname) (where hostname should print out the current container id) signals the buildscript to change the hostname for that container. The build script then queries the pid for the container and executes hostname 'new-hostname' in its uts namespace. The sleep 1 just gives the build script some time to adjust the hostname properly. Then I modify /etc/hostname and /etc/hosts to include the newly set hostname.

This even changes the output of uname -n so I am pretty sure it would work as a solution for the original question.

like image 174
LolHens Avatar answered Oct 21 '22 20:10

LolHens


Let me see if I understand your question, you would like to build an image that when run as a container has the runtime hostname even if the hostname used for building is not the same. Correct? If so, my question to you is the following, are you able to reconfigure the software to have a new hostname after it is installed?

If this is possible, I would recommend writing a script that is able to modify the hostname and use this script as an ENTRYPOINT. This way you can guarantee that you have corrected the hostname anytime your container is run (with any command) and you don't spend time trying to force support for a particular hostname through at build time, which, by your own admission, is difficult to do.

like image 40
jakirkham Avatar answered Oct 21 '22 18:10

jakirkham


You can use docker-compose to build your image and assign the hostname, e.g.

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

The run as: docker-compose --build up.

like image 3
kenorb Avatar answered Oct 21 '22 19:10

kenorb