[Docker Network Chapter 3] Understand the -net = host option

image.png

series

[Docker Network Chapter 1] [Docker Network Chapter 2]

Introduction

Containers are now a required feature in every development cycle. Understanding how container networks work is essential to us. This is not only important from a service communication perspective, but also because it forms an important aspect of infrastructure security.

In this article, we'll take a quick look at the different network modes available in Docker containers and take a closer look at hosted mode networks.

Network mode overview

None None is simple in that the container receives the network stack, but it has no external network interface. Instead, it receives a loopback interface. Both the rkt container project and the Docker container project provide similar behavior when using a None or Null network. This mode of container networking has many uses, such as testing containers, staging containers for later network connectivity, and allocating to containers that do not require external communication.

Bridge mode

The Linux bridge provides a host internal network through which containers on the same host can communicate, but the IP address assigned to each container is not accessible from outside the host. Bridge networking leverages iptables for NAT and port mapping, which provides single-host networking. The bridge network is the default Docker network type (that is, docker0), with one end of the virtual network interface pair connected between the bridge and the container.

The following is an example of the creation flow.

  1. The bridge is provisioned on the host.
  2. The namespace for each container is provisioned within that bridge.
  3. The container's ethX is mapped to the private bridge interface.
  4. Iptables with NAT is used to map between each private container and the host's public interface.

NAT is used to provide communication across hosts. Bridged networks solve the problem of port contention and provide network isolation for containers running on one host, but with the performance costs associated with using NAT.

Host mode

In this approach, the newly created container shares its network namespace with the host, providing better performance and eliminating the need for NAT. However, the disadvantage is port contention. The container has access to all of the host's network interfaces, but the container may not reconfigure the host's network stack unless it is deployed in privileged mode.

Host network is the default type used within Mesos. That is, if the framework does not specify a network type, the new network namespace will be associated with the host network instead of the container. Host networks, sometimes referred to as native networks, are conceptually simple and easy to understand, troubleshoot, and use.

overlay

Overlays use network tunnels to provide communication between hosts. This allows containers to behave as if they were on the same machine by tunneling network subnets from one host to the next. In essence, a network spans multiple hosts. There are many tunneling technologies, such as Virtual Extensible Local Area Networks (VXLAN).

VXLAN is the tunneling technology of choice for Dockerlibnetwork, which introduced multihost networks as a native feature in the 1.9 release. With the introduction of this feature, Docker has chosen to leverage HashiCorp's Serf as its gossip protocol. This was chosen because of the efficiency of neighbor table exchange and convergence time.

If you need support for other tunneling technologies, Flannel is for you. It supports udp, vxlan, host-gw, aws-vpc, or gce, and for each cloud provider tunnel type, a route is created in the provider's routing table specifically for your account or virtual private cloud (VPC). Public cloud support is especially important for overlay drivers, especially given that overlays are best suited for hybrid cloud use cases and provide scaling and redundancy without opening public ports.

Multi-host networks require additional parameters and a key-value store when starting the Docker daemon. Some overlays rely on the distributed key value store. If you are doing container orchestration, you already have a decentralized key-value store.

Because overlays focus on communication challenges between hosts, containers on the same host connected to two different overlay networks communicate with each other over a local bridge because they are segmented from each other. can not.

Underlay

The underlay network driver exposes the host interface (that is, the eth0 physical network interface) directly to a container or VM running on the host. Two such underlay drivers are the Media Access Control Virtual Local Area Network (MACvlan) and the Internet Protocol VLAN (IPvlan). The operation and operation of MACvlan and IPvlan drivers is very familiar to network engineers. Both network drivers are conceptually simpler than bridge networks, eliminating the need for port mapping and being more efficient. In addition, IPvlan has an L3 mode, which has resonated with many network engineers. Given most public cloud limitations (or lack of functionality), underlays are especially useful when you need to address on-premises workloads, security concerns, traffic priorities, or compliance, Brownfield. Ideal for use in. Underlay networking allows one VLAN per subinterface instead of requiring one bridge per VLAN.

Understand the details of hosted mode networks

Host mode looks very simple, but there are some things to keep in mind when deploying. Let's look at an example and see what we are talking about. First, let's start a basic web container on the host.

docker run -d --name=web1 --net=host vaibhavthakur/docker:webinstance1

I am passing the "–net = host" flag in the docker run command. Also, no port mapping is specified. When the image is downloaded, docker runs the image as a container called the "web". Now that we've instructed docker to run this container as a daemon, it's time to connect to the container's bash shell. ‍

docker exec -it web1 /bin/bash

This requires you to go to the shell inside the container and see all the network adapters available in the container. You can do this as shown below.

root@docker-1:~# docker exec -it web1 /bin/bash
[root@docker-1 /]# ifconfig
docker0   Link encap:Ethernet  HWaddr 02:42:9F:DE:3C:3C  
          inet addr:172.17.0.1  Bcast:172.17.255.255  Mask:255.255.0.0
          inet6 addr: fe80::42:9fff:fede:3c3c/64 Scope:Link
          UP BROADCAST MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:2 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:0 (0.0 b)  TX bytes:180 (180.0 b)

ens4      Link encap:Ethernet  HWaddr 42:01:0A:80:0F:E8  
          inet addr:10.128.15.232  Bcast:0.0.0.0  Mask:255.255.255.255
          inet6 addr: fe80::4001:aff:fe80:fe8/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1460  Metric:1
          RX packets:99119 errors:0 dropped:0 overruns:0 frame:0
          TX packets:10016 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:236101229 (225.1 MiB)  TX bytes:881725 (861.0 KiB)

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:336 errors:0 dropped:0 overruns:0 frame:0
          TX packets:336 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:35505 (34.6 KiB)  TX bytes:35505 (34.6 KiB)

Now you need to exit the container and run the same command on the host.

root@docker-1:~# ifconfig
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:9fff:fede:3c3c  prefixlen 64  scopeid 0x20<link>
        ether 02:42:9f:de:3c:3c  txqueuelen 0  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 2  bytes 180 (180.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

ens4: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1460
        inet 10.128.15.232  netmask 255.255.255.255  broadcast 0.0.0.0
        inet6 fe80::4001:aff:fe80:fe8  prefixlen 64  scopeid 0x20<link>
        ether 42:01:0a:80:0f:e8  txqueuelen 1000  (Ethernet)
        RX packets 99198  bytes 236116665 (236.1 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 10074  bytes 888616 (888.6 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 336  bytes 35505 (35.5 KB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 336  bytes 35505 (35.5 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

root@docker-1:~# 

You can see that the output of the command is exactly the same. This means that you need to be able to access the container using the host IP. If you are using a cloud provider, be aware that the firewall rule you accept on port 80 is enabled on your machine.

Let's access the service with the public IP of the instance. You can see the response from the service running inside the web1 container.

image.png

Note that you did not provide port mapping while the container was running, and you explicitly specified the host node network so that you can access the Apache server running inside the container on port 80.

Now let's try something else. Run another container for the same service on this host using a hosted mode network to see what happens.

docker run -it --name web2 --net=host vaibhavthakur/docker:webinstance2

If you look carefully, the container didn't even start. Let's take a look at the log.

root@docker-1:~# docker run -it --name web2 --net=host vaibhavthakur/docker:webinstance2
Unable to find image vaibhavthakur/docker:webinstance2' locally
webinstance2: Pulling from vaibhavthakur/docker
Image docker.io/jonlangemak/docker:webinstance2 uses outdated schema1 manifest format. Please upgrade to a schema2 image for better future compatibility. More information at https://docs.docker.com/registry/spec/deprecated-schema-v1/
a3ed95caeb02: Already exists 
3a5831f32025: Already exists 
1070eeab2b98: Already exists 
86ba7b11cc42: Already exists 
8ea0f21db18b: Already exists 
772c76531fbf: Already exists 
04120008298a: Already exists 
6a221adc032a: Already exists 
4a835ccd3c00: Already exists 
78e705e491cf: Already exists 
c343940bb393: Already exists 
663a5158a2b7: Already exists 
09cf647f3fa5: Pull complete 
Digest: sha256:62883de5c6d7786ec4267b8a0ccb9b104dea7523c9db934ecb911b78a5d983ee
Status: Downloaded newer image for vaibhavthakur/docker:webinstance2
2019-12-26 04:57:29,606 CRIT Supervisor running as root (no user in config file)
2019-12-26 04:57:29,619 INFO supervisord started with pid 1
2019-12-26 04:57:29,621 INFO spawned: 'httpd' with pid 6
2019-12-26 04:57:29,664 INFO exited: httpd (exit status 1; not expected)
2019-12-26 04:57:29,665 INFO received SIGCLD indicating a child quit
2019-12-26 04:57:30,668 INFO spawned: 'httpd' with pid 7
2019-12-26 04:57:30,707 INFO exited: httpd (exit status 1; not expected)
2019-12-26 04:57:30,707 INFO received SIGCLD indicating a child quit
2019-12-26 04:57:32,711 INFO spawned: 'httpd' with pid 8
2019-12-26 04:57:32,749 INFO exited: httpd (exit status 1; not expected)
2019-12-26 04:57:32,750 INFO received SIGCLD indicating a child quit
2019-12-26 04:57:35,755 INFO spawned: 'httpd' with pid 9
2019-12-26 04:57:35,796 INFO exited: httpd (exit status 1; not expected)
2019-12-26 04:57:35,796 INFO received SIGCLD indicating a child quit
2019-12-26 04:57:36,798 INFO gave up: httpd entered FATAL state, too many start retries too quickly

Therefore, this is a clear indication that the container could not be started because the entry point process terminated immediately. Let's dig a little deeper and get inside the container.

root@docker-1:~# docker exec -it web2 /bin/bash
[root@docker-1 /]# service httpd status
httpd dead but subsys locked
[root@docker-1 /]# service httpd start
Starting httpd: (98)Address already in use: make_sock: could not bind to address [::]:80
(98)Address already in use: make_sock: could not bind to address 0.0.0.0:80
no listening sockets available, shutting down
Unable to open logs
                                                           [FAILED]

When I first checked the status of the httpd service, I found that it was stopped. Then I tried to boot, but it failed again because I couldn't bind to port 80. Let's run the test again to check the status of port 80.

[root@docker-1 /]# netstat -plnt
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address               Foreign Address             State       PID/Program name   
tcp        0      0 127.0.0.53:53               0.0.0.0:*                   LISTEN      -                   
tcp        0      0 0.0.0.0:22                  0.0.0.0:*                   LISTEN      -                   
tcp        0      0 :::80                       :::*                        LISTEN      -                   
tcp        0      0 :::22                       :::*                        LISTEN      -                   
[root@docker-1 /]# 

You can clearly see that port 80 is occupied by some process. This process is a container named web1. So you can conclude that the web2 container tried to bind to port 80 on the same host where web1 is running and failed. Now let's kill the web1 container and start the httpd service in the web2 container.

After forcibly terminating web1, I started httpd on web2 and succeeded.

[root@docker-1 /]# service httpd start
Starting httpd:                                            [  OK  ]
[root@docker-1 /]# 

As a final test, let's access the instance on port 80.

image.png

I was able to see the response from the service running inside the web2 container.

Summary

It is important to understand that hosted mode networks improve the network performance of containers, as network traffic does not have to go through the docker0 bridge and iptables port mapping. In this article, we have clearly explained what a hosted mode network is and how it differs from other network options. If you have any questions, please feel free to contact us.

If you are considering monitoring the container, you can monitor it with Hosted Prometheus via Metric Fire. Sign up for a Free Trial (https://www.hostedgraphite.com/accounts/signup-metricfire/?signup=japan&utm_source=blog&utm_medium=Qiita&utm_campaign=Japan&utm_content=Understanding%20Docker's%20-net%3Dhost%20Option) here or book a demo (https://www.metricfire.com/demo-japan/?GAID=2038175526.1567400116&utm_source=blog&utm_medium=Qiita&utm_campaign=Japan&utm_content=Understanding%20Docker's%20-net%3Dhost%20Option) and contact us directly. For more information, see the three-part series on Docker networks. This series details different types of Docker networks.

Recommended Posts

[Docker Network Chapter 3] Understand the -net = host option
[Docker Network Chapter 2] Explanation of Docker Networking
[Docker Network Chapter 1] Explanation of Docker Networking
Understand Docker