Docker networking on single host with compose - docker

I am trying to run docker networking with compose in docker 1.9. I know this is still experimental in 1.9, but I'd like to know if my use case could work.
Basically and to simplify, I have two servers, each one being in a container. Let's call them S1 and S2. S1 is exposed globally and must be accessible from S2. I want to run S2 through docker-compose with the --x-networking flag (the reason why I want this is that S2 is actually a bit more complexe than what is assumed here, having several containers, and I want them to run in a single subnetwork). S1 can run with or without compose.
What I understand from docker networks is that any container can reach other from the same network, or can reach anything that is "globally" exposed through host port mapping, right?
So my scenario is:
I start manually S1 with port mapping such as "-p 7210:7202" (7202 is exposed in dockerfile)
S2 is created from a simple compose file and gone up with flag --x-networking
For my test case I just created a very minimalistic compose file, such as:
S2:
build: .
ports:
- "8080:80"
Results:
S1 is NOT visible from S2 under "localhost" (this is quite expected)
S1 is NOT visible from S2 under "172.17.0.1" (= interface docker0)
I would have expected to be able to reach it under 172.17.0.1, since S1 uses docker0 as I understand.
Moreover, if I start S2 without compose but manually with "docker run", then I can access S1 using 172.17.0.1
So why doesn't it work with compose? Is it a limitation due to networking features being still experimental?

Note:
This is old content. The --x-networking has been removed in docker-compose
1.6. Additionally Docker compose has a new file format.
The documentation states that the networking feature is experimental in Docker compose.
Note: Compose’s networking support is experimental, and must be
explicitly enabled with the docker-compose --x-networking flag.
It's Docker 1.9 that actually implements the new feature:
https://docs.docker.com/engine/userguide/networking/dockernetworks/
Perhaps an example would help.
Example
└── demo
└── docker-compose.yml
I'm using docker 1.9.0 and docker-compose 1.5.0
docker-compose.yml
Declare two containers callled "web1" and "web2". Doesn't matter what image in this case I'm running tomcat.
web1:
image: tomcat:8.0
ports:
- 8080
web2:
image: tomcat:8.0
ports:
- 8080
Create the containers
Start this using docker compose
$ cd demo
$ docker-compose --x-networking up -d
Creating network "demo" with driver "None"
Creating demo_web2_1
Creating demo_web1_1
Note how a network called "demo" is created when you specified the new --x-networking parameter.
Demonstrate how discovery works
Each container is run on the "demo" network that was created and each container is placed as an entry in the other hosts file.
$ docker-compose ps
Name Command State Ports
---------------------------------------------------------------
demo_web1_1 catalina.sh run Up 0.0.0.0:32773->8080/tcp
demo_web2_1 catalina.sh run Up 0.0.0.0:32772->8080/tcp
$ docker exec -it demo_web1_1 cat /etc/hosts
..
172.18.0.2 demo_web2_1
$ docker exec -it demo_web2_1 cat /etc/hosts
..
172.18.0.3 demo_web1_1
Run an additional container outside of compose
Start another container, and specify you want it to be attached to the "demo" network:
$ docker run --net demo --name helloworld -d tomcat:8.0
And see how the hosts files in the other containers is updated automatically
$ docker exec -it demo_web1_1 cat /etc/hosts
..
172.18.0.2 demo_web2_1
172.18.0.4 helloworld
$ docker exec -it demo_web2_1 cat /etc/hosts
172.18.0.3 demo_web1_1
172.18.0.4 helloworld
$ docker exec -it helloworld cat /etc/hosts
172.18.0.2 demo_web2_1
172.18.0.3 demo_web1_1

Related

Restart entire docker compose stack from one of the containers

Is there any proper way of restarting an entire docker compose stack from within one of its containers?
One workaround involves mounting the docker socket:
volumes:
- /var/run/docker.sock:/var/run/docker.sock
and then use the Docker Engine SDKs (https://docs.docker.com/engine/api/sdk/examples/).
However, this solution only allows restarting the containers itselves. There seems to be no way to send compose commands, like docker compose restart, docker compose up, etc.
The only solution I've found to send docker compose commands is to open a terminal on the host from the container using ssh, like this: access host's ssh tunnel from docker container
This is partly related to How to run shell script on host from docker container? , but I'm actually looking for a more specific solution to only send docker compose commands.
I tried with this simple docker-compose.yml file
version: '3'
services:
nginx:
image: nginx
ports:
- 3000:80
Then I started a docker container using
docker run -it --rm -v /var/run/docker.sock:/var/run/docker.sock -v $(pwd):/work docker
Then, inside the container, I did
cd /work
docker-compose up -d
and it started the container up on the host.
Please note that you have an error in your socket mapping. It needs to be
- /var/run/docker.sock:/var/run/docker.sock
(you have a period instead of a slash at one point)
As mentioned by #BMitch in the comments, compose project name was the reason why I wasn't able to run docker compose commands inside the running container.
By default the compose project name is set to the directory name, so if the docker-compose.yml is run from a host directory named folder1, then the commands inside the container should be run as:
docker-compose -p folder1 ...
So now, for example, restarting the stack works:
docker-compose -p folder1 restart
Just as a reference, a fixed project name for your compose can be set using name: ... as a top-level attribute of the .yml file, but requires docker compose v2.3.3 : Set $PROJECT_NAME in docker-compose file

What is the difference between docker run -p and ports in docker-compose.yml?

I would like to use a standard way of running my docker containers. I have have been keeping a docker_run.sh file, but docker-compose.yml looks like a better choice. This seems to work great until I try to access my website running in the container. The ports don't seem to be set up correctly.
Using the following docker_run.sh, I can access the website at localhost. I expected the following docker-compose.yml file to have the same results when I use the docker-compose run web command.
docker_run.sh
docker build -t web .
docker run -it -v /home/<user>/git/www:/var/www -p 80:80/tcp -p 443:443/tcp -p 3316:3306/tcp web
docker-compose.yml
version: '3'
services:
web:
image: web
build: .
ports:
- "80:80"
- "443:443"
- "3316:3306"
volumes:
- "../www:/var/www"
Further analysis
The ports are reported as the same in docker ps and docker-compose ps. Note: these were not up at the same time.
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
<id> web "/usr/local/scripts/…" About an hour ago Up About an hour 0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp, 0.0.0.0:3307->3306/tcp <name>
$ docker-compose ps
Name Command State Ports
---------------------------------------------------------------------------------------------------------------
web /usr/local/scripts/start_s ... Up 0.0.0.0:3316->3306/tcp, 0.0.0.0:443->443/tcp, 0.0.0.0:80->80/tcp
What am I missing?
As #richyen suggests in a comment, you want docker-compose up instead of docker-compose run.
docker-compose run...
Runs a one-time command against a service.
That is, it's intended to run something like a debugging shell or a migration script, in the overall environment specified by the docker-compose.yml file, but not the standard command specified in the Dockerfile (or the override in the YAML file).
Critically to your question,
...docker-compose run [...] does not create any of the ports specified in the service configuration. This prevents port collisions with already-open ports. If you do want the service’s ports to be created and mapped to the host, specify the --service-ports flag.
Beyond that, the docker run command you show and the docker-compose.yml file should be essentially equivalent.
You don't run docker-compose.yamls the same way that you would run a local docker image that you have either installed or created on your machine. docker-compose files are typically launched running the command docker-compose up -d to run in detached mode. Then when you run docker ps you should see it running. You can also run docker-compose ps as you did above.

Connect to docker-compose network using docker run

Let say I have running orchestration with docker-compose with docker-compose.yml looking like this:
version: '2.2'
services:
service1:
# ...
networks:
- compose_network
service2:
# ...
networks:
- compose_network
networks:
compose_network:
I aim to run and connect temporarily one container to compose_network_1. I tried using
$ docker run --net=compose_network <image for the job>
but I could not connect. I am also aware that docker-compose names the networks as [projectname]_default, so I also tried that variant, but with same result.
Is there a way I can accomplish that?
I'm not sure if the --net option ever existed but it's now --network.
From docker run --help:
--network string Connect a container to a network (default "default")
As #maxm notes you can find the network name, with the DIR prefix of the compose project directory, then simply run it as you were trying:
$ docker run --network=DIR_compose_network <image for the job>
I wanted to connect on run as my container is transient (running tests) so I can't use a second docker network command in time before it quits.
e.g. for my docker composition in a "dev" folder with no network name specified so uses the docker-compose "default" name, therefore I get the name dev_default.
docker network ls
NETWORK ID NAME DRIVER SCOPE
2c660d9ed0ba bridge bridge local
b81db348e773 dev_default bridge local
ecb0eb6e93a5 host host local
docker run -it --network dev_default myimage
This connects the new docker container to the existing docker-compose network.
The network name is going to be something like name-of-directory_compose_network. Find the name with docker network ls
I had success with:
docker-compose up # within directory ./demo
docker run -itd -p "8000:8000" --hostname=hello "crccheck/hello-world"
# outputs: 1e502f65070c9e2da7615c5175d5fc00c49ebdcb18962ea83a0b24ee0440da2b
docker network connect --alias hello demo_compose_network 1e502f65070c
I could then curl hello:8000 from inside my docker compose containers. Should be the exact same functionality as your commands, just with an added alias.

Host resolution with docker and docker-compose

I'm wondering why, with docker-compose, the container cannot resole host-name while it's working with docker ? The host is on different physical machine but on the same network.
My Dockerfile
FROM openjdk:8-jre-alpine
CMD ping -c 2 myhost
My docker-compose.yml
version: '3.3'
services:
net:
build: .
image: test/myimage:1.0
container_name: mycontainer
After a docker-compose build I tried
docker run -it test/myimage:1.0
PING myhost (10.20.78.13): 56 data bytes
64 bytes from 10.20.78.13: seq=0 ttl=250 time=0.720 ms
64 bytes from 10.20.78.13: seq=1 ttl=250 time=0.515 ms
but
docker-compose up
Recreating mycontainer ...
Recreating mycontainer ... done
Attaching to mycontainer
mycontainer | ping: bad address 'myhost'
mycontainer exited with code 1
What can I do to have it working ?
Edit1
Using cat /etc/resolv.conf instead of ping -c 2 myhost
docker-compose up
Recreating mycontainer ...
Recreating mycontainer ... done
Attaching to mycontainer
mycontainer | search myorg.intra
mycontainer | nameserver 127.0.0.11
mycontainer | options ndots:0
mycontainer exited with code 0
and
docker run -it test/myimage:1.0
# Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8)
# DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN
nameserver 10.19.128.55
nameserver 10.19.142.23
nameserver 10.19.128.56
search myorg.intra
You need use the dns entries in your compose also
version: '3.3'
services:
net:
build: .
image: test/myimage:1.0
container_name: mycontainer
dns:
- 10.19.128.55
- 10.19.142.23
- 10.19.128.56
dns_search: myorg.intra
If you don't want to specify them per container then you can even set them directly at the daemon level.
Create or edit /etc/docker/daemon.json and below entries in the JSON file
{
"dns": ["10.19.128.55", "10.19.142.23", "10.19.128.56"],
"dns-search": ["myorg.intra"],
}
Restart the docker service using service docker restart. Then you should not need the entries in the docker-compose. Your original docker-compose should work
There is a subtle difference in network settings when running docker commands directly vs. performing some docker-compose runs. With docker-compose the containers are attached to some user-defined network which is a bridged network but not with the same configuration as the default bridged network. The latter one does some special configuration to be backwards compatible to the versions where these networking features didn't exist like today.
I assume that when running from compose, you are missing some dns settings that are available on your host machine and thus you aren't able to resolve the other host on your network. Please have a look at the differences here and here. For a first check, you could compare the output when running cat /etc/resolv.conf instead of your ping command for both ways and see what you need to add to make it work from compose.

How can I add hostnames to a container on the same docker network?

Suppose I have a docker compose file with two containers. Both reference each other in their /etc/hosts file. Container A has a reference for container B and vice versa. And all of this happens automatically. Now I want to add one or more hostnames to B in A's hosts file. How can I go about doing this? Is there a special way I can achieve this in Docker Compose?
Example:
172.0.10.166 service-b my-custom-hostname
Yes. In your compose file, you can specify network aliases.
services:
db:
networks:
default:
aliases:
- database
- postgres
In this example, the db service could be reached by other containers on the default network using db, database, or postgres.
You can also add aliases to running containers using the docker network connect command with the --alias= option.
Docker compose has an extra_hosts feature that allows additional entries to be added to the container's host file.
Example
docker-compose.yml
web1:
image: tomcat:8.0
ports:
- 8081:8080
extra_hosts:
- "somehost:162.242.195.82"
- "otherhost:50.31.209.229"
web2:
image: tomcat:8.0
ports:
- 8082:8080
web3:
image: tomcat:8.0
ports:
- 8083:8080
Demonstrate host file entries
Run docker compose with the new docker 1.9 networking feature:
$ docker-compose --x-networking up -d
Starting tmp_web1_1
Starting tmp_web2_1
Starting tmp_web3_1
and look at the hosts file in the first container. Shows the other containers, plus the additional custom entries:
$ docker exec tmp_web1_1 cat /etc/hosts
..
172.18.0.4 web1
172.18.0.2 tmp_web2_1
172.18.0.3 tmp_web3_1
50.31.209.229 otherhost
162.242.195.82 somehost
If I understand your question correctly, you can pass a host name referenced in your host's /etc/hosts file via --add-host flag :
$ docker run ... --add-host="droid"
Your host's /etc/hosts would need the following entry:
xx.xx.xx.xx droid
Of course, xx.xx.xx.xx will need to be reachable from inside the container you just started using the 'docker run' command. You can have one or more --add-host="xyz".
More details about --add-host here:
http://docs.docker.com/v1.8/reference/commandline/run/

Resources