Network Drivers
List all networks (the list below is the default network created by Docker since we have not created any networks yet). Reference
Ex:
$ docker network ls
Result:
NETWORK ID NAME DRIVER SCOPE
09a5f34a0bb8 bridge bridge local
f419db6a9e21 host host local
0175d39fdfe6 none null local
*bridge:
The default network driver.
If you don’t specify a driver, this is the type of network you are creating.
Bridge networks are usually used when your applications run in standalone containers that need to communicate.
*host:
For standalone containers, remove network isolation between the container and the Docker host, and use the host’s networking directly.
*none(null):
For this container, disable all networking.
Usually used in conjunction with a custom network driver.
Bridge network
*In terms of networking:
A bridge network is a Link Layer device which forwards traffic between network segments.
*In terms of Docker:
A bridge network uses a software bridge which allows containers connected to the same bridge network to communicate. It provides isolation from containers which are not connected to that bridge network.
Default bridge
When you start Docker, a default bridge network (also called bridge) is created automatically, and newly-started containers connect to it unless otherwise specified
First of all, let's check our network interface in our docker host.
Ex:
$ ifconfig
Result:
docker0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
inet 172.17.0.1 netmask 255.255.0.0 broadcast 172.17.255.255
inet6 fe80::42:62ff:fe8b:caa7 prefixlen 64 scopeid 0x20<link>
ether 02:42:62:8b:ca:a7 txqueuelen 0 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 9 bytes 1041 (1.0 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
'docker0' is the interface Docker created for us for default bridge network.
Ex:
$ docker network inspect bridge
Result:
[
{
"Name": "bridge",
"Scope": "local",
"Driver": "bridge",
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "172.17.0.0/16",
"Gateway": "172.17.0.1"
}
]
},
"Containers": {},
"Labels": {},
...
}
]
Therefore, the gateway of the default bridge will be docker0 in Docker host.
Then, let's create two container without specifying network.
Ex:
$ docker container run -d -it --name alpine_default_1 alpine sh
$ docker container run -d -it --name alpine_default_2 alpine sh
Result:
bd334a1aa24f97f61d048a7f459b8e7a93ac479805993f2b510fbae47c88abb7
93122bbae5a31bf10041b2fd473244d34621ee563eabf496d33ba7346e10b8e5
Check the detail of each container separately.
Ex:
$ docker container inspect bd33
Result:
"NetworkSettings": {
...
"Networks": {
"bridge": {
"IPAMConfig": null,
"Links": null,
"Aliases": null,
"NetworkID": "09a5f34a0bb8118d5f064a6496c939a1ece5a817...",
"EndpointID": "755aa48ce8fdc5d6c750cd71f541d40069b2b06...",
"Gateway": "172.17.0.1",
"IPAddress": "172.17.0.2",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:ac:11:00:02",
"DriverOpts": null
}
}
}
Ex:
$ docker container inspect 9312
Result:
"NetworkSettings": {
...
"Networks": {
"bridge": {
"IPAMConfig": null,
"Links": null,
"Aliases": null,
"NetworkID": "09a5f34a0bb8118d5f064a6496c939a1ece5a817...",
"EndpointID": "9b9d9e1efa86c27ee1dd20f70bed3776f7f15ce...",
"Gateway": "172.17.0.1",
"IPAddress": "172.17.0.3",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:ac:11:00:03",
"DriverOpts": null
}
}
}
Ex:
$ docker network inspect bridge
Result:
[
{
"Name": "bridge",
"Scope": "local",
"Driver": "bridge",
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "172.17.0.0/16",
"Gateway": "172.17.0.1"
}
]
},
"Containers": {
"93122bbae5a31bf10041b2fd473244d34621ee563eabf496d...": {
"Name": "alpine_default_2",
"EndpointID": "9b9d9e1efa86c27ee1dd20f70bed3776f7f15ce7...",
"MacAddress": "02:42:ac:11:00:03",
"IPv4Address": "172.17.0.3/16",
"IPv6Address": ""
},
"bd334a1aa24f97f61d048a7f459b8e7a93ac479805993f2b...": {
"Name": "alpine_default_1",
"EndpointID": "755aa48ce8fdc5d6c750cd71f541d40069b2b069...",
"MacAddress": "02:42:ac:11:00:02",
"IPv4Address": "172.17.0.2/16",
"IPv6Address": ""
}
},
"Labels": {}
}
]
Once it is all set, let's verify the connection between them.
Let's try to ping containers by IP address first.
Ex:
$ docker container exec -it alpine_default_1 sh
/ # ping -c 1 172.17.0.3
Result:
PING 172.17.0.3 (172.17.0.3): 56 data bytes
64 bytes from 172.17.0.3: seq=0 ttl=64 time=0.056 ms
--- 172.17.0.3 ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 0.056/0.056/0.056 ms
Let's try to ping containers by the container name.
Ex:
/ # ping -c 1 alpine_default_2
Result:
ping: bad address 'alpine_default_2'
Opps, since we are using the default bridge, we cannot use container name to find its IP address automatically.
User-defined Bridge
Docker allows us to create user-defined bridge, and actually the document indicates that we should use user-defined bridge instead of default bridge for those reasons.
Lets' create a user-defined bridge to dive deeper.
Ex:
$ docker network create my-bridge
Result:
14988e726d808d3af667636c0e8051de92bca0d80a64f0f90aa65cbd555575fb
Ex:
$ docker network ls
Result:
NETWORK ID NAME DRIVER SCOPE
09a5f34a0bb8 bridge bridge local
f419db6a9e21 host host local
14988e726d80 my-bridge bridge local
0175d39fdfe6 none null local
Ex:
$ docker network inspect my-bridge
Result:
[
{
"Name": "my-bridge",
"Scope": "local",
"Driver": "bridge",
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.18.0.0/16",
"Gateway": "172.18.0.1"
}
]
},
"Containers": {},
"Labels": {}
}
]
Ex:
$ ifconfig
Result:
br-14988e726d80: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
inet 172.18.0.1 netmask 255.255.0.0 broadcast 172.18.255.255
ether 02:42:67:8c:86:48 txqueuelen 0 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.17.0.1 netmask 255.255.0.0 broadcast 172.17.255.255
inet6 fe80::42:62ff:fe8b:caa7 prefixlen 64 scopeid 0x20<link>
ether 02:42:62:8b:ca:a7 txqueuelen 0 (Ethernet)
RX packets 6 bytes 308 (308.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 15 bytes 1644 (1.6 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
The gateway of the new created user-defined bridge will be br-14988e726d80 network in Docker host.
Then create containers and specify the network with this user-defined bridge.
Ex:
$ docker container run -d -it --name alpine_user_defined_3 \
--network my-bridge alpine sh
$ docker container run -d -it --name alpine_user_defined_4 \
--network my-bridge alpine sh
Result:
8b43bd1b39467d8cacaf6a20e5f7c1cacbee288c55a3bfb73dba9cd89a9a6a6c
2e91d9793bc1fb4d740c5ca4df9a8c22f8fe4616c3c63ad388943bb248e464c0
Ex:
$ docker container inspect 8b43
Result:
"NetworkSettings": {
"Networks": {
"my-bridge": {
"IPAMConfig": null,
"Links": null,
"Aliases": [
"8b43bd1b3946"
],
"NetworkID": "14988e726d808d3af667636c0e8051de92bca0d80...",
"EndpointID": "fa8c9a1bbf57750bfed8c23049fdce56f7da7575...",
"Gateway": "172.18.0.1",
"IPAddress": "172.18.0.2",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:ac:12:00:02",
"DriverOpts": null
}
}
}
Ex:
$ docker container inspect 2e91
Result:
"NetworkSettings": {
"Networks": {
"my-bridge": {
"IPAMConfig": null,
"Links": null,
"Aliases": [
"2e91d9793bc1"
],
"NetworkID": "14988e726d808d3af667636c0e8051de92bca0d80...",
"EndpointID": "f91c5cda192c8a9c48f2a46630ab542758dc0fe1...",
"Gateway": "172.18.0.1",
"IPAddress": "172.18.0.3",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:ac:12:00:03",
"DriverOpts": null
}
}
}
Since we are using user-undefined bridge, we can use container name now.
Ex:
$ docker container exec -it alpine_user_defined_3 sh
/ # ping -c 1 alpine_user_defined_4
Result:
PING alpine_user_defined_4 (172.18.0.3): 56 data bytes
64 bytes from 172.18.0.3: seq=0 ttl=64 time=0.135 ms
--- alpine_user_defined_4 ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 0.135/0.135/0.135 ms
Port Expose
From the Dockerfile, the EXPOSE command is just as a type of documentation.
It informs the developers that the container created by this image will need to listens on the specified network ports at runtime.
Therefore, when running your container, you need to use -p to publish ports.
The exposed port is accessible on the host and the ports are available to any client that can reach the host. Reference
Ex:
$ docker container run -d --name web1 \
--network my-bridge -p 8080:80 nginx
Result:
f8594b7f3db8824386923a38664e92b70363dfb8c82c45d46cb1e370aff6f3dc
Then we can access it with that port in our Docker host.
Ex:
$ wget localhost:8080
Result:
Resolving localhost (localhost)... 127.0.0.1
Connecting to localhost (localhost)|127.0.0.1|:8080... connected.
HTTP request sent, awaiting response... 200 OK
Length: 615 [text/html]
index.html 100%[===============>] 615 --.-KB/s in 0s
Let's check the iptables to see what rules Docker added.
Ex:
$ sudo iptables -t nat -nvxL
Result:
Chain DOCKER (2 references)
target prot opt source destination
RETURN all -- 0.0.0.0/0 0.0.0.0/0
RETURN all -- 0.0.0.0/0 0.0.0.0/0
DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:8080 to:172.18.0.2:80