Verifying host-level settings that impact Docker networking

Docker relies on the host being capable of performing certain functions to make Docker networking work. Namely, your Linux host must be configured to allow IP forwarding. In addition, since the release of Docker 1.7, you may now choose to use hairpin Network Address Translation (NAT) rather than the default Docker user land proxy. In this recipe, we'll review the requirement for the host to have IP forwarding enabled. We'll also talk about NAT hairpin and discuss the host-level requirements for that option as well. In both cases, we'll show Docker's default behavior with regard to its settings as well as how you can alter them.

Getting ready

You'll need access to a Linux host running Docker and the ability to stop and restart the service. Since we'll be modifying system-level kernel parameters, you'll also need root-level access to the system.

How to do it…

As we saw in Chapter 1, Linux Networking Constructs, a Linux host must have IP forwarding enabled to be able to route traffic between interfaces. Since Docker does just that, IP forwarding must be enabled for Docker networking to function as desired. If Docker detects that IP forwarding is disabled, it will warn you of the issue when you attempt to run a container:

user@docker1:~$ docker run --name web1 -it 
jonlangemak/web_server_1 /bin/bash
WARNING: IPv4 forwarding is disabled. Networking will not work.
root@071d673821b8:/#

Most Linux distributions default the IP forward value to disabled or 0. Fortunately for us, in a default configuration, Docker takes care of updating this setting to the correct value when the Docker service starts. For instance, let's take a look at a freshly rebooted host that doesn't have the Docker service enabled at boot time. If we check the value of the setting before starting Docker, we can see that it's disabled. Starting the Docker engine automatically enables the setting for us:

user@docker1:~$ more /proc/sys/net/ipv4/ip_forward
0
user@docker1:~$
user@docker1:~$ sudo systemctl start docker
user@docker1:~$ sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 1
user@docker1:~$

This default behavior in Docker can be changed by passing --ip-forward=false as a runtime option to the Docker service.

Note

The configuration of Docker-specific parameters varies widely based on the init system used. At the time of writing, many newer Linux operating systems use systemd as their init system. Always consult the Docker documentation to see its recommendation for service configuration based on the operating system you are using. Docker service configuration and options are talked about in greater detail as part of an upcoming recipe in this chapter. In this recipe, just focus on the impact changing these settings has on both Docker and the host itself.

Further discussion on the kernel IP forward parameter can be found in the recipe Configuring Linux host routing in Chapter 1, Linux Networking Constructs. There you'll find how to update the parameter yourself as well as how to make the setting persistent through reboots.

Another recent feature of Docker that relies on a kernel-level parameter is the hairpin NAT functionality. Earlier versions of Docker implemented, and relied on, what's known as the Docker userland proxy to facilitate intercontainer and published port communication. By default, any containers exposing ports did so through the userland proxy process. For instance, if we start an example container we can see that in addition to the Docker process itself, we also now have a docker-proxy process:

user@docker1:~$ docker run --name web1 -d -P jonlangemak/web_server_1
bf3cb30e826ce53e6e7db4e72af71f15b2b8f83bd6892e4838ec0a59b17ac33f
user@docker1:~$
user@docker1:~$ ps aux | grep docker
root       771  0.0  0.1 509676 41656 ?        Ssl  19:30   0:00 /usr/bin/docker daemon
root      1861  0.2  0.0 117532 28024 ?        Sl   19:41   0:00 docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 32769 -container-ip 172.17.0.2 -container-port 80
…<Additional output removed for brevity>…
user@docker1:~$

Every published port will start a new docker-proxy process on the Docker host. As an alternative to the userland proxy, you have the option to have Docker use hairpin NAT rather than userland proxies. Hairpin NAT relies on the host system being configured to enable routing on the host's local loopback interfaces. Again, the Docker service takes care of updating the correct host parameter to enable this functionality when the Docker service starts if it's told to do so.

Hairpin NAT relies on the kernel parameter net.ipv4.conf.docker0.route_localnet being enabled (set to 1) in order for the host machine to access container services through the hosts loopback interface. This can be achieved in the same way as we described with the IP forward parameter:

Using the sysctl command:

sysctl net.ipv4.conf.docker0.route_localnet 

By querying the /proc/ filesystem directly:

more /proc/sys/net/ipv4/conf/docker0/route_localnet

If the returned value is 0, it's likely that Docker is in its default configuration and is relying on the userland proxy. Since you have the option to run Docker in either mode, we need to do more than change the kernel parameters in order to make the change to hairpin NAT. We also need to tell Docker to change the way it publishes ports by passing the option --userland-proxy=false as a runtime option to the Docker service. Doing so will enable hairpin NAT and also tell Docker to update the kernel parameter to the correct setting for hairpin NAT to work. Let's enable hairpin NAT to validate that Docker is doing what it should be doing.

First, let's check the value of the kernel parameter:

user@docker1:~$ sysctl net.ipv4.conf.docker0.route_localnet
net.ipv4.conf.docker0.route_localnet = 0
user@docker1:~$

It's currently disabled. Now we can tell Docker to disable the userland proxy by passing the --userland-proxy=false as a parameter to the Docker service. Once the Docker service is told to disable the userland proxy, and the service is restarted, we should see that the parameter is enabled on the host:

user@docker1:~$ sysctl net.ipv4.conf.docker0.route_localnet
net.ipv4.conf.docker0.route_localnet = 1
user@docker1:~$

Running a container with a mapped port at this point will not create additional docker-proxy process instances:

user@docker1:~$ docker run --name web1 -d -P jonlangemak/web_server_1
5743fac364fadb3d86f66cb65532691fe926af545639da18f82a94fd35683c54
user@docker1:~$ ps aux | grep docker
root      2159  0.1  0.1 310696 34880 ?        Ssl  14:26   0:00 /usr/bin/docker daemon --userland-proxy=false
user@docker1:~$

In addition, we are still able to access the container through the host's local interface:

user@docker1:~$ curl 127.0.0.1:32768
<body>
  <html>
    <h1><span style="color:#FF0000;font-size:72px;">Web Server #1 - Running on port 80</span>
    </h1>
</body>
  </html>
user@docker1:~$

Disabling the parameter once again causes this connection to fail:

user@docker1:~$ sudo sysctl -w net.ipv4.conf.docker0.route_localnet=0
net.ipv4.conf.docker0.route_localnet = 0
user@docker1:~$ curl 127.0.0.1:32768
curl: (7) Failed to connect to 127.0.0.1 port 32768: Connection timed out
user@docker1:~$
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset