I am developing a service and using there docker compose to spin services like postgres, redis, elasticsearch. I have a web application that is based on RubyOnRails and writes and reads from all those services.
Here is my docker-compose.yml
version: '2' services: redis: image: redis:2.8 networks: - frontapp elasticsearch: image: elasticsearch:2.2 networks: - frontapp postgres: image: postgres:9.5 environment: POSTGRES_USER: elephant POSTGRES_PASSWORD: smarty_pants POSTGRES_DB: elephant volumes: - /var/lib/postgresql/data networks: - frontapp networks: frontapp: driver: bridge
And i can ping containers within this network
$ docker-compose run redis /bin/bash root@777501e06c03:/data# ping postgres PING postgres (172.20.0.2): 56 data bytes 64 bytes from 172.20.0.2: icmp_seq=0 ttl=64 time=0.346 ms 64 bytes from 172.20.0.2: icmp_seq=1 ttl=64 time=0.047 ms ...
So far so good. Now I want to run ruby on rails application on my host machine but be able to access postgres instance with url like postgresql://username:password@postgres/database
currently that is not possible
$ ping postgres ping: unknown host postgres
I can see my network in docker
$ docker network ls NETWORK ID NAME DRIVER ac394b85ce09 bridge bridge 0189d7e86b33 elephant_default bridge 7e00c70bde3b elephant_frontapp bridge a648554a72fa host host 4ad9f0f41b36 none null
And I can see an interface to it
$ ifconfig br-0189d7e86b33 Link encap:Ethernet HWaddr 02:42:76:72:bb:c2 inet addr:172.18.0.1 Bcast:0.0.0.0 Mask:255.255.0.0 inet6 addr: fe80::42:76ff:fe72:bbc2/64 Scope:Link UP BROADCAST MULTICAST MTU:1500 Metric:1 RX packets:36 errors:0 dropped:0 overruns:0 frame:0 TX packets:60 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:2000 (2.0 KB) TX bytes:8792 (8.7 KB) br-7e00c70bde3b Link encap:Ethernet HWaddr 02:42:e7:d1:fe:29 inet addr:172.20.0.1 Bcast:0.0.0.0 Mask:255.255.0.0 inet6 addr: fe80::42:e7ff:fed1:fe29/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:1584 errors:0 dropped:0 overruns:0 frame:0 TX packets:1597 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:407137 (407.1 KB) TX bytes:292299 (292.2 KB) ...
But i am not sure what should I do next. I tried to play a bit with /etc/resolv.conf
, mainly with nameserver
directive, but that had no effect.
I would appreciate any help of suggestions how to configure this setup correctly.
UPDATE
After browsing through Internet resources I managed to assign static IP addresses to boxes. For now it is enough for me to continue development. Here is my current docker-compose.yml
version: '2' services: redis: image: redis:2.8 networks: frontapp: ipv4_address: 172.25.0.11 elasticsearch: image: elasticsearch:2.2 networks: frontapp: ipv4_address: 172.25.0.12 postgres: image: postgres:9.5 environment: POSTGRES_USER: elephant POSTGRES_PASSWORD: smarty_pants POSTGRES_DB: elephant volumes: - /var/lib/postgresql/data networks: frontapp: ipv4_address: 172.25.0.10 networks: frontapp: driver: bridge ipam: driver: default config: - subnet: 172.25.0.0/16 gateway: 172.25.0.1
Accessing the Host With the Default Bridge Mode You just need to reference it by its Docker network IP, instead of localhost or 127.0. 0.1 . Your host's Docker IP will be shown on the inet line. Connect to this IP address from within your containers to successfully access the services running on your host.
You need some DNS to map container ip:s to hostnames. If you want out of the box solution. One solution is to use for example Kontena. It comes with network overlay technology from Weave and this technology is used to create virtual private LAN networks for each service and every service can be reached by service_name.
You can assign memorable names to your docker containers when you run them, using the --name flag as follows. The -d flag tells docker to run a container in detached mode, in the background and print the new container ID. To view a list of all your docker containers, run the following command.
There is a opensource application that solves this issue, it's called DNS Proxy Server, here some examples from official repository
It's a DNS server that solves containers hostnames, if could not found a hostname that matches then solve it from internet as well
$ docker run --hostname dns.mageddo --restart=unless-stopped -p 5380:5380 \ -v /var/run/docker.sock:/var/run/docker.sock \ -v /etc/resolv.conf:/etc/resolv.conf \ defreitas/dns-proxy-server
It will be set automatically as your default DNS (and recover to the original when it stops)
checking docker-compose file
$ cat docker-compose.yml version: '3' services: nginx-1: image: nginx hostname: nginx-1.docker network_mode: bridge linux-1: image: alpine hostname: linux-1.docker command: sh -c 'apk add --update bind-tools && tail -f /dev/null' network_mode: bridge # that way he can solve others containers names even inside, solve nginx-2, for example
starting containers
$ docker-compose up
from host
nslookup nginx-1.docker Server: 13.0.0.5 Address: 13.0.0.5#53 Non-authoritative answer: Name: nginx-1.docker Address: 13.0.0.6
from another container
$ docker-compose exec linux-1 ping nginx-1.docker PING nginx-1.docker (13.0.0.6): 56 data bytes 64 bytes from 13.0.0.6: seq=0 ttl=64 time=0.034 ms
As well it solves internet hostnames
$ nslookup google.com Server: 13.0.0.5 Address: 13.0.0.5#53 Non-authoritative answer: Name: google.com Address: 216.58.202.78
I'm using a bash script to update /etc/hosts
. Why this solution?
docker events
to run every time a container is started or stopped (other solutions posted here run every second in a loop, which is way less efficient)/etc/hosts
, no separate DNS server needed.bash
, mktemp
, grep
, xargs
, sed
, jq
and docker
, all of which I had already installed.Just put the script somewhere, e.g. /usr/local/bin/docker-update-hosts
:
#!/usr/bin/env bash set -e -u -o pipefail hosts_file=/etc/hosts begin_block="# BEGIN DOCKER CONTAINERS" end_block="# END DOCKER CONTAINERS" if ! grep -Fxq "$begin_block" "$hosts_file"; then echo -e "\n${begin_block}\n${end_block}\n" >> "$hosts_file" fi (echo "| container start |" && docker events) | \ while read event; do if [[ "$event" == *" container start "* ]] || [[ "$event" == *" network disconnect "* ]]; then hosts_file_tmp="$(mktemp)" docker container ls -q | xargs -r docker container inspect | \ jq -r '.[]|"\(.NetworkSettings.Networks[].IPAddress|select(length > 0) // "# no ip address:") \(.Name|sub("^/"; "")|sub("_1$"; ""))"' | \ sed -ne "/^${begin_block}$/ {p; r /dev/stdin" -e ":a; n; /^${end_block}$/ {p; b}; ba}; p" "$hosts_file" \ > "$hosts_file_tmp" chmod 644 "$hosts_file_tmp" mv "$hosts_file_tmp" "$hosts_file" fi done
Note: The script removes the _1
suffix added by docker-compose from container names. If you don't want that just remove |sub("_1$"; "")
from the script.
You can use a systemd service to run this synchronously with Docker: /etc/systemd/system/docker-update-hosts.service
:
[Unit] Description=Update Docker containers in /etc/hosts Requires=docker.service After=docker.service PartOf=docker.service [Service] ExecStart=/usr/local/bin/docker-update-hosts [Install] WantedBy=docker.service
To activate, run:
systemctl daemon-reload systemctl enable docker-update-hosts.service systemctl start docker-update-hosts.service
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With