What's the different between host mode and bridge mode? - linux

When I use the host mode to run mycontainer, it doesn't work,but the bridge mode works.I'd like to ask what's the different between the two modes?
Run with host mode:
docker run --name=zhiwenyi --net=host -d [image]
Run with bridge mode:docker run --name=zhiwenyi -d -p 35229:35229 [image]
Dockerfile:
FROM java:8
VOLUME /data/log/upload
COPY target/upload_V6_20220722.jar upload.jar
EXPOSE 35229
RUN bash -c "touch /upload.jar"
ENTRYPOINT ["java","-Xmx512m","-Xms512m","-jar","-Duser.timezone=GMT+08","-Dfile.encoding=utf-8","upload.jar"]
In bridge mode, I send a post request to myip:35229/path ,it works well.
In host mode, the same request ,it shows me connection time out.
Supply:
OS:centos 7.9
Docker:20.10.17

If you use host mode it disables the container's network namespace.
A bridge network in the context of Docker employs a software bridge to
provide isolation from containers that are not linked to that bridge
network while enabling communication between containers that are
connected to the same bridge network.
The network stack of a container that uses the host network driver is
not separated from the Docker host. For instance, if you utilize host
networking and run a container that binds to port 80, the application
for the container will be accessible on port 80 on the host's IP
address.

Related

Docker-compose: connect from Docker container to linux host

In "naked" Docker, I can use --network=host to make the host reachable from the container through the docker0 interface at 127.0.0.1, or I can reach the host at 172.17.0.1.
Ostensibly docker-compose supports the host networking driver through network_mode: host. Unfortunately, this prevents me from using exposed ports. Containers created using docker-compose don't live on the docker0 interface, and hence can reach the host at some IP address like 172.18.0.1 or 172.19.0.1, decided at startup.
Even once I know that address, it doesn't seem to be reachable from the container.
How can I connect from the container to the host?

Bind docker container loopback to the host loopback

I pull a docker image (will use python 3 as an example)
docker pull python:3.6
Then I launch a docker container
docker run -it -p 127.0.0.1:8000:8000 python:3.6 bash
(note that here 127.0.0.1 in 127.0.0.1:8000:8000 allows to specify the destination, host IP but not the source)
So if I launch a server inside the container at 0.0.0.0:
python -m http.server 8000 --bind 0.0.0.0
then I can access the container's server from the host machine without any problem by going to http://127.0.0.1:8000 at the host machine
However if my docker server binds to 127.0.0.1 instead of 0.0.0.0:
python -m http.server 8000 --bind 127.0.0.1
then accessing http://127.0.0.1:8000 from the host does not work.
What's the proper way of binding the container's loopback 127.0.0.1 to the host loopback?
What's the proper way of binding the container's loopback 127.0.0.1 to the host loopback?
On Linux, this can be done by configuring your Docker container to use the hosts network namespace, ie:
docker run --network=host
This only works on Linux because on Linux, your machine is the host, and the containers run as containers in your machines OS. On Windows/OSX, the Docker host runs as a virtual machine, with the containers running in the virtual machine, and so they can't share your machines network namespace.
What's the proper way of binding the container's loopback 127.0.0.1 to the host loopback?
You can't do that. The loopback interface inside a container means "only this container", just like on the host means "only this host". If a service is binding to 127.0.0.1 then there is no way -- from your host or from another container -- to reach that service.
The only way to do what you want is either:
Modify the application configuration to listen on all interfaces (or eth0 specifically), or
Run a proxy inside your container that binds to all interfaces and forwards connections to the localhost address.

What does localhost means inside a Docker container?

Say, if I use this command inside a docker container.
/opt/lampp/bin/mysql -h localhost -u root -pThePassword
What would the localhost here refer to? The host machine's IP or the docker container's own IP?
From inside a container, localhost always refers to the current container. It never refers to another container, and it never refers to anything else running on your physical system that's not in the same container. It's not usually useful to make outbound connections to localhost or configure localhost as your database host.
From a shell on your host system, localhost could refer to daemons running on your system outside Docker, or to ports you've published with docker run -p options.
From a different system, localhost refers to the system it's called from.
In terms of IP addresses, localhost is always 127.0.0.1, and that IP address is special and is always localhost and behaves the same way as above.
If you want to make a connection to a container...
...from another container, the best way is to make sure they're on the same Docker network (you started them from the same Docker Compose YAML file; you did a docker network create and then did docker run --net ... on the same network) and use Docker's internal DNS service to refer to them by the container's --name or its name in the Docker Compose YAML file and the port number inside the container. Even if the target has a published port with a docker run -p option or Docker Compose ports: setting, use the second (container-internal) port number.
...from outside Docker space, make sure you started the container with a docker run -p or Docker Compose ports: option, and connect to the host's IP address or DNS name using the first port number from that option.
...from a terminal window or browser on the same physical host, not in a container, in this case and in this case only, localhost will work consistently.
Except:
If you started a container with --net host, localhost refers to the physical host, and you're in the "terminal window on the same physical host" scenario.
If you've gone out of your way to have multiple servers in the same container, you can use localhost to communicate between them.
If you're running in Kubernetes, and you have multiple containers in the same pod, you can use localhost to communicate between them. Between pods, you should set up a service in front of each pod/deployment, and use DNS names of the form service-name.namespace-name.svc.cluster.local.
Definitely, It will be your container, if you are running command in container.
/opt/lampp/bin/mysql -h localhost -u root -pThePassword
If you run this command inside container then it will try to connect mysql running inside container.

Run dnsmasq as DHCP server from inside a Docker container

I'm trying to get dnsmasq to operate as a DHCP server inside a Docker container, issuing DHCP addresses to machines on the host's physical network. I'm using the Alpine Linux 6MB container from https://hub.docker.com/r/andyshinn/dnsmasq/.
It works fine as a DNS server on port 53 on the host machine, however there is nothing listening on port 67/udp, which is where I'm expecting DHCP to be. I use
dhcping 192.168.2.2, but get "no answer". telnet 192.168.2.2 67 returns "Connection refused".
My dnsmasq.conf file in the container looks like this:
interface=eth0
user=root
domain-needed
bogus-priv
no-resolv
local=/mydomain.io/
no-poll
server=8.8.8.8
server=8.8.4.4
no-hosts
addn-hosts=/etc/dnsmasq_static_hosts.conf
expand-hosts
domain=mydomain.io
dhcp-range=192.168.2.10,192.168.2.250,255.255.255.0,192.168.2.255,5m
# Have windows machine release on shutdown
dhcp-option=vendor:MSFT,2,1i
# No default route
dhcp-option=3
The host machine has a static address of 192.168.2.2.
I start the container like this:
docker run -d --name dns -p 192.168.2.2:67:67/udp -p 192.168.2.2:53:53/udp sitapati/dns
There is no firewall on this machine, which is running Ubuntu 16.04.
Things I've thought of/tried:
is it because eth0 in the container has an address on a completely different subnet? (docker inspect tells me it's 172.17.0.2 on the bridged interface)
does it need to use --net host? I tried that, and it still didn't work.
Yes, the container will have its own interfaces on a virtual subnet (the docker0 bridge network). So it will be trying to offer addresses on that subnet.
Using --net host worked for me, I got the DHCP server working using something like the following command:
docker run --name dnsmasq2 -t -v /vagrant/dnsmasq.conf:/opt/dnsmasq.conf -p 67:67/udp --net host centos
--net host ensures that the container appears to using the host's networking stack rather than its own.
dnsmasq -q -d --conf-file=/opt/dnsmasq.conf --dhcp-broadcast
I also needed to add the --dhcp-broadcast flag to dnsmasq within the container to get it to actually broadcast DHCPOFFER messages on the network. For some reason, dnsmasq was trying to unicast the DHCPOFFER messages, and it was using ARP to try to get an address that had not yet been assigned.

Forward host port to docker container

Is it possible to have a Docker container access ports opened by the host? Concretely I have MongoDB and RabbitMQ running on the host and I'd like to run a process in a Docker container to listen to the queue and (optionally) write to the database.
I know I can forward a port from the container to the host (via the -p option) and have a connection to the outside world (i.e. internet) from within the Docker container but I'd like to not expose the RabbitMQ and MongoDB ports from the host to the outside world.
EDIT: some clarification:
Starting Nmap 5.21 ( http://nmap.org ) at 2013-07-22 22:39 CEST
Nmap scan report for localhost (127.0.0.1)
Host is up (0.00027s latency).
PORT STATE SERVICE
6311/tcp open unknown
joelkuiper#vps20528 ~ % docker run -i -t base /bin/bash
root#f043b4b235a7:/# apt-get install nmap
root#f043b4b235a7:/# nmap 172.16.42.1 -p 6311 # IP found via docker inspect -> gateway
Starting Nmap 6.00 ( http://nmap.org ) at 2013-07-22 20:43 UTC
Nmap scan report for 172.16.42.1
Host is up (0.000060s latency).
PORT STATE SERVICE
6311/tcp filtered unknown
MAC Address: E2:69:9C:11:42:65 (Unknown)
Nmap done: 1 IP address (1 host up) scanned in 13.31 seconds
I had to do this trick to get any internet connection within the container: My firewall is blocking network connections from the docker container to outside
EDIT: Eventually I went with creating a custom bridge using pipework and having the services listen on the bridge IP's. I went with this approach instead of having MongoDB and RabbitMQ listen on the docker bridge because it gives more flexibility.
A simple but relatively insecure way would be to use the --net=host option to docker run.
This option makes it so that the container uses the networking stack of the host. Then you can connect to services running on the host simply by using "localhost" as the hostname.
This is easier to configure because you won't have to configure the service to accept connections from the IP address of your docker container, and you won't have to tell the docker container a specific IP address or host name to connect to, just a port.
For example, you can test it out by running the following command, which assumes your image is called my_image, your image includes the telnet utility, and the service you want to connect to is on port 25:
docker run --rm -i -t --net=host my_image telnet localhost 25
If you consider doing it this way, please see the caution about security on this page:
https://docs.docker.com/articles/networking/
It says:
--net=host -- Tells Docker to skip placing the container inside of a separate network stack. In essence, this choice tells Docker to not containerize the container's networking! While container processes will still be confined to their own filesystem and process list and resource limits, a quick ip addr command will show you that, network-wise, they live “outside” in the main Docker host and have full access to its network interfaces. Note that this does not let the container reconfigure the host network stack — that would require --privileged=true — but it does let container processes open low-numbered ports like any other root process. It also allows the container to access local network services like D-bus. This can lead to processes in the container being able to do unexpected things like restart your computer. You should use this option with caution.
Your docker host exposes an adapter to all the containers. Assuming you are on recent ubuntu, you can run
ip addr
This will give you a list of network adapters, one of which will look something like
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP
link/ether 22:23:6b:28:6b:e0 brd ff:ff:ff:ff:ff:ff
inet 172.17.42.1/16 scope global docker0
inet6 fe80::a402:65ff:fe86:bba6/64 scope link
valid_lft forever preferred_lft forever
You will need to tell rabbit/mongo to bind to that IP (172.17.42.1). After that, you should be able to open connections to 172.17.42.1 from within your containers.
As stated in one of the comments, this works for Mac (probably for Windows/Linux too):
I WANT TO CONNECT FROM A CONTAINER TO A SERVICE ON THE HOST
The host has a changing IP address (or none if you have no network access). We recommend that you connect to the special DNS name host.docker.internal which resolves to the internal IP address used by the host. This is for development purpose and will not work in a production environment outside of Docker Desktop for Mac.
You can also reach the gateway using gateway.docker.internal.
Quoted from https://docs.docker.com/docker-for-mac/networking/
This worked for me without using --net=host.
You could also create an ssh tunnel.
docker-compose.yml:
---
version: '2'
services:
kibana:
image: "kibana:4.5.1"
links:
- elasticsearch
volumes:
- ./config/kibana:/opt/kibana/config:ro
elasticsearch:
build:
context: .
dockerfile: ./docker/Dockerfile.tunnel
entrypoint: ssh
command: "-N elasticsearch -L 0.0.0.0:9200:localhost:9200"
docker/Dockerfile.tunnel:
FROM buildpack-deps:jessie
RUN apt-get update && \
DEBIAN_FRONTEND=noninteractive \
apt-get -y install ssh && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
COPY ./config/ssh/id_rsa /root/.ssh/id_rsa
COPY ./config/ssh/config /root/.ssh/config
COPY ./config/ssh/known_hosts /root/.ssh/known_hosts
RUN chmod 600 /root/.ssh/id_rsa && \
chmod 600 /root/.ssh/config && \
chown $USER:$USER -R /root/.ssh
config/ssh/config:
# Elasticsearch Server
Host elasticsearch
HostName jump.host.czerasz.com
User czerasz
ForwardAgent yes
IdentityFile ~/.ssh/id_rsa
This way the elasticsearch has a tunnel to the server with the running service (Elasticsearch, MongoDB, PostgreSQL) and exposes port 9200 with that service.
TLDR;
For local development only, do the following:
Start the service or SSH tunnel on your laptop/computer/PC/Mac.
Build/run your Docker image/container to connect to hostname host.docker.internal:<hostPort>
Note: There is also gateway.docker.internal, which I have not tried.
END_TLDR;
For example, if you were using this in your container:
PGPASSWORD=password psql -h localhost -p 5432 -d mydb -U myuser
change it to this:
PGPASSWORD=password psql -h host.docker.internal -p 5432 -d mydb -U myuser
This magically connects to the service running on my host machine. You do not need to use --net=host or -p "hostPort:ContainerPort" or -P
Background
For details see: https://docs.docker.com/docker-for-mac/networking/#use-cases-and-workarounds
I used this with an SSH tunnel to an AWS RDS Postgres Instance on Windows 10. I only had to change from using localhost:containerPort in the container to host.docker.internal:hostPort.
I had a similar problem accessing a LDAP-Server from a docker container.
I set a fixed IP for the container and added a firewall rule.
docker-compose.yml:
version: '2'
services:
containerName:
image: dockerImageName:latest
extra_hosts:
- "dockerhost:192.168.50.1"
networks:
my_net:
ipv4_address: 192.168.50.2
networks:
my_net:
ipam:
config:
- subnet: 192.168.50.0/24
iptables rule:
iptables -A INPUT -j ACCEPT -p tcp -s 192.168.50.2 -d $192.168.50.1 --dport portnumberOnHost
Inside the container access dockerhost:portnumberOnHost
If MongoDB and RabbitMQ are running on the Host, then the port should already exposed as it is not within Docker.
You do not need the -p option in order to expose ports from container to host. By default, all port are exposed. The -p option allows you to expose a port from the container to the outside of the host.
So, my guess is that you do not need -p at all and it should be working fine :)
For newer versions of Docker, this worked for me. Create the tunnel like this (notice the 0.0.0.0 at the start):
-L 0.0.0.0:8080:localhost:8081
This will allow anyone with access to your computer to connect to port 8080 and thus access port 8081 on the connected server.
Then, inside the container just use "host.docker.internal", for example:
curl host.docker.internal:8081
why not use slightly different solution, like this?
services:
kubefwd:
image: txn2/kubefwd
command: ...
app:
image: bash
command:
- sleep
- inf
init: true
network_mode: service:kubefwd
REF: txn2/kubefwd: Bulk port forwarding Kubernetes services for local development.
Easier way under all platforms nowadays is to use host.docker.internal. Let's first start with the Docker run command:
docker run --add-host=host.docker.internal:host-gateway [....]
Or add the following to your service, when using Docker Compose:
extra_hosts:
- "host.docker.internal:host-gateway"
Full example of such a Docker Compose file should then look like this:
version: "3"
services:
your_service:
image: username/docker_image_name
restart: always
networks:
- your_bridge_network
volumes:
- /home/user/test.json:/app/test.json
ports:
- "8080:80"
extra_hosts:
- "host.docker.internal:host-gateway"
networks:
your_bridge_network:
Again, it's just an example. But in if this docker image will start a service on port 80, it will be available on the host on port 8080.
And more importantly for your use-case; if the Docker container want to use a service from your host system that would now be possible using the special host.docker.internal name. That name will automatically be resolved into the internal Docker IP address (of the docker0 interface).
Anyway, let's say... you also running a web service on your host machine on (port 80). You should now be able to reach that service within your Docker container.. Try it out: nc -vz host.docker.internal 80.
All WITHOUT using network_mode: "host".

Resources