Local hostnames for Docker containers

DockerDocker Compose

Docker Problem Overview


Beginner Docker question here,

So I have a development environment in which I'm running a modular app, it is working using Docker Compose to run 3 containers: server, client, database.

The docker-compose.yml looks like this:

#############################
# Server
#############################
server:
  container_name: server
  domainname: server.dev
  hostname: server
  build: ./server
  working_dir: /app
  ports:
    - "3000:3000"
  volumes:
    - ./server:/app
  links:
    - database

#############################
# Client
#############################
client:
  container_name: client
  domainname: client.dev
  hostname: client
  image: php:5.6-apache
  ports:
     - "80:80"
  volumes:
   - ./client:/var/www/html

#############################
# Database
#############################
database:
  container_name: database
  domainname: database.dev
  hostname: database
  image: postgres:9.4
  restart: always
  environment:
    - POSTGRES_USER=postgres
    - POSTGRES_PASSWORD=root
    - POSTGRES_DB=dbdev
    - PG_TRUST_LOCALNET=true
  ports:
    - "5432:5432"
  volumes:
    - ./database/scripts:/docker-entrypoint-initdb.d # init scripts

You can see I'm assigning a .dev domainname to each one, this works fine to see one machine from another one (Docker internal network), for example here I'm pinging server.dev from client.dev's CLI:

    root@client:/var/www/html# ping server.dev
    PING server.dev (127.0.53.53): 56 data bytes
    64 bytes from 127.0.53.53: icmp_seq=0 ttl=64 time=0.036 ms

This works great internally, but not on my host OS network.

For convenience, I would like to assigns domains in MY local network, not the Docker containers network so that I can for example type: client.dev on my browsers URL and load the Docker container.

Right now, I can only access if I use the Docker IP, which is dynamic:

client: 192.168.99.100:80
server: 192.168.99.100:3000
database: 192.168.99.100:5432

Is there an automated/convenient way to do this that doesn't involve me manually adding the IP to my /etc/hosts file ?

BTW I'm on OSX if that has any relevance.

Thanks!

Edit: I found this Github issue which seems to be related: https://github.com/docker/docker/issues/2335

As far as I understood, they seem to say that it is something that is not available outside of the box and they suggest external tools like:

Is that correct? And if so, which one should I go for in my particular scenario?

Docker Solutions


Solution 1 - Docker

OK,

so since it seems that there is no native way to do this with Docker, I finally opted for this alternate solution from Ryan Armstrong, which consists in dynamically updating the /etc/hosts file.

I chose this since it was convenient for me since this works as a script, and I already had a startup script, so I could just append this function in to it.

> The following example creates a hosts entry named docker.local which > will resolve to your docker-machine IP: > > update-docker-host(){ > # clear existing docker.local entry from /etc/hosts > sudo sed -i '' '/[[:space:]]docker.local$/d' /etc/hosts > > # get ip of running machine > export DOCKER_IP="$(echo ${DOCKER_HOST} | grep -oE '[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}')" > > # update /etc/hosts with docker machine ip > [[ -n $DOCKER_IP ]] && sudo /bin/bash -c "echo "${DOCKER_IP} docker.local" >> /etc/hosts" > } > > update-docker-host

This will automatically add or udpate the /etc/hosts line on my host OS when I start the Docker machine through my startup script.

Anyways, as I found out during my research, apart from editing the hosts file, you could also solve this problem by setting up a custom DNS server:

Also found several projects on Github which apparently aim to solve this problem, although I didn't try them:

Solution 2 - Docker

Extending on @eduwass's own answer, here's what I did manually (without a script).

  1. As mentioned in the question, define the domainname: myapp.dev and hostname: www in the docker-compose.yml file
  2. Bring up your Docker containers as normal
  3. Run docker-compose exec client cat /etc/hosts to get an output of the container's hosts file (where client is your service name) (Output example: 172.18.0.6 www.myapp.dev)
  4. Open your local (host machine) /etc/hosts file and add that line: 172.18.0.6 server.server.dev

If your Docker service container changes IPs or does anything fancy you will want a more complex solution, but this is working for my simple needs at the moment.

Solution 3 - Docker

Another solution would be to use a browser with a proxy extension sending the requests through a proxy container that will know where to resolve the domains to. If you consider using jwilder/nginx-proxy for production mode, then your issue can be easily solved with mitm-nginx-proxy-companion.

Here is an example based on your original stack:

version: '3.3'

services:

  server:
    build: ./server
    working_dir: /app
    volumes:
      - ./server:/app

  client:
    environment:
      - VIRTUAL_HOST: client.dev
    image: php:5.6-apache
    volumes:
    - ./client:/var/www/html

  database:
    image: postgres:9.4
    restart: always
    environment:
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=root
      - POSTGRES_DB=dbdev
      - PG_TRUST_LOCALNET=true
    volumes:
      - ./database/scripts:/docker-entrypoint-initdb.d # init scripts

  nginx-proxy:
    image: jwilder/nginx-proxy
    labels:
      - "mitmproxy.proxyVirtualHosts=true"
    volumes:
      - /var/run/docker.sock:/tmp/docker.sock:ro

  nginx-proxy-mitm:
    dns:
      - 127.0.0.1
    image: artemkloko/mitm-nginx-proxy-companion
    ports:
      - "8080:8080"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
  • Run docker-compose up
  • Add a proxy extension to your browser, with proxy address being 127.0.0.1:8080
  • Access http://client.dev

The request will follow the route:

  • Access a local development domain in a browser
  • The proxy extension forwards that request to mitm-nginx-proxy-companion instead of the “real” internet
  • mitm-nginx-proxy-companion tries to resolve the domain name through the dns server in the same container
    • If the domain is not a “local” one, it will forward the request to the “real” internet
    • But if the domain is a “local” one, it will forward the request to the nginx-proxy
  • The nginx-proxy in its turn forwards the request to the appropriate container that includes the service we want to access

Side notes:

  • links removed as it's outdated and is replaced by Docker networks

  • you don't need to add domain names to server and database containers. client will be able to access them on server and database domains because they are all in the same network (similar to what link was doing previously)

  • you don't need to use ports on server and database containers because it only forwards ports to be used through 127.0.0.1. PHP in client container will do only "back-end" requests to other containers, and because those containers are in the same network, you already can access them with database:5432 and server:3000. The same goes for server <-> database connections.

  • I am the author of mitm-nginx-proxy-companion

Solution 4 - Docker

In order to make whole domain for localhost you can use dnsmasq. In this case if you chose the domain .dev any subdomain will point to your container. But you have to know about problems with .dev zone

Or you can use bash script for launch your docker-compose which on start will add line to /etc/hosts and after you kill this process this line will removed

#!/usr/bin/env bash

sudo sed -i '1s;^;127.0.0.1    example.dev\n;' /etc/hosts

trap 'sudo sed -i "/example.dev/d" /etc/hosts' 2

docker-compose up

Solution 5 - Docker

My Bash script WITH ALIAS without docker-machine Based on http://cavaliercoder.com/blog/update-etc-hosts-for-docker-machine.html


#!/bin/bash




#alias
declare -A aliasArr
aliasArr[docker_name]="alias1,alias2"



clear existing *.docker.local entries from /etc/hosts



sudo sed -i '/.docker.local$/d' /etc/hosts



iterate over each machine



docker ps -a --format "{{.Names}}" 

| while read -r MACHINE; do



MACHINE_IP="$(docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' ${MACHINE} 2>/dev/null)"

if [[ ${aliasArr[$MACHINE]} ]]
then
    DOMAIN_NAME=$(echo ${aliasArr[$MACHINE]} | tr "," "\n")
else
    DOMAIN_NAME=( ${MACHINE} )
fi

for addr in $DOMAIN_NAME
do
    echo "add ${MACHINE_IP}	${addr}.docker.local"
    [[ -n $MACHINE_IP ]] && sudo /bin/bash -c "echo \"${MACHINE_IP}	${addr}.docker.local\" >> /etc/hosts"
    export no_proxy=$no_proxy,$MACHINE_IP
done
    




done


Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionEdu WassView Question on Stackoverflow
Solution 1 - DockerEdu WassView Answer on Stackoverflow
Solution 2 - DockerthaddeusmtView Answer on Stackoverflow
Solution 3 - DockerArtem TitkovView Answer on Stackoverflow
Solution 4 - DockeranydasaView Answer on Stackoverflow
Solution 5 - Dockeruser194125View Answer on Stackoverflow