2021年5月12日 星期三

[ 文章收集 ] Connection refused? Docker networking and how it impacts your image

 Source From Here

Preface
Can’t connect to the server running in your container? Let’s see why, and how to fix it, starting with an example.

If you run a server on your machine listening on 127.0.0.1, the “loopback” or “localhost” address:
$ python3 -m http.server --bind 127.0.0.1
Serving HTTP on 127.0.0.1 port 8000 (http://127.0.0.1:8000/) ...

You can then load it in your browser at http://127.0.0.1:8000.

But if you kill that and run it in a container:
$ docker run -p 8000:8000 -it --entrypoint=bash python:3.7-slim
root@85bdb39291b3:/# python3 -m http.server --bind 127.0.0.1
Serving HTTP on 127.0.0.1 port 8000 (http://127.0.0.1:8000/) ...

If you then try to connect with your browser to http://127.0.0.1:8000 you’ll get connection refused or connection reset.

What’s going on? To understand how to solve this, you need to know a minimal amount about how Docker’s networking works. In particular, this article will cover:
* Networking namespaces, and how Docker uses them.
* What docker run -p 5000:5000 does, and why our example above doesn’t work.
* How to fix your image so the server is accessible.

Networking without Docker
Let’s start with our first scenario: you run a server directly inside your operating system, and then connect to it. I’m going to assume the main OS is Linux, for simplicity of explanation. Docker runs on non-Linux OSes like macOS by running a Linux virtual machine, but the practical consequences are the same.

Your operating system has multiple network “interfaces”. For example, on my computer (with output shortened for clarity):
$ ifconfig
  1. docker0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500  
  2.   inet 172.17.0.1  
  3.   
  4. lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536  
  5.   inet 127.0.0.1  
  6.   
  7. wlp0s20u8: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500  
  8.   inet 192.168.7.202  

In this output we see three network interfaces:
* We’ll ignore docker0 for now.
* lo is the loopback interface, with IPv4 address 127.0.0.1: it’s your own computer, addressable in-memory without any networking hardware.
* wlp0s20u8 is my WiFi card, with IPv4 address 192.168.7.202, and when I talk to computers on the Internet the packets are sent via that interface.

Let’s go back to our starting, working example—you run a server listening on 127.0.0.1, and then connect to it. We can visualize it like this:


Network namespaces
You’ll notice the image above talks about a “Default network namespace”. So what’s that?

Docker is a system for running containers: a way to isolate processes from each other. It builds on a number of Linux kernel features, one of which is network namespaces—a way for different processes to have different network devices, IPs, firewall rules, and so on.

By default, each container run by Docker has its own network namespace, with its own IPs:
$ docker run --rm -it busybox
/ # ifconfig
  1. eth0      Link encap:Ethernet  HWaddr 02:42:AC:11:00:02  
  2.           inet addr:172.17.0.2  Bcast:172.17.255.255  Mask:255.255.0.0  
  3.   
  4. lo        Link encap:Local Loopback  
  5.           inet addr:127.0.0.1  Mask:255.0.0.0  

So this container has two interfaces, eth0 and lo, each with their own IP addresses. But because this is a different network namespace, these are different interfaces than the default namespace we saw above. To make it clear what this means, let’s run the Flask server inside a Docker container, and then diagram the results:
$ docker run --rm example
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

The resulting network setup looks like this:


Now it’s clear why there’s a connection refused: the server is listening on 127.0.0.1 inside the container’s network namespace. The browser is connecting to 127.0.0.1 in the main, default network namespace. But those are different interfaces, so no connection is made.

How do we connect the two network namespaces? With Docker port-forwarding.

Docker run port-forwarding (is not enough)
If we run docker run with -p 5000:5000, it will forward from all interfaces where the Docker daemon is running (for our purposes, the main network namespace) to the external IP address of the container.

To break it down explicitly: -p 5000:5000 means redirecting traffic from port 5000 on all interfaces in the main network namespace to the container’s port 5000 on its external interface. -p 8080:80 would redirect traffic from port 8080 on all interfaces in the main network namespace to port 80 on the container’s external interface. And so on.

(We’re doing port 5000 specifically because that’s where our Docker image is listening, Flask’s default port.)

So let’s run a container, and then look at a diagram to visually see what that means:
$ docker run --rm -p 5000:5000 example
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)



And now we see the second problem: the server is listening on 127.0.0.1 inside the container network namespace, but the port forwarding is going to the external IP, 172.17.0.2.

Thus, a connection reset or refused.

The solution: listen on all interfaces
Port forwarding can only connect to a single destination—but you can change where the server process is listening. You do this by listening on 0.0.0.0, which means “listen on all interfaces”.

For example, you can do:
$ docker run -p 8000:8000 -it python:3.7-slim python3 -m http.server --bind 0.0.0.0

Note: --bind 0.0.0.0 is specifically an option for http.server; it’s not a Docker option. Other servers will have other ways of specifying this. For example, for a Flask application packaged with a Dockerfile, you can do:
  1. FROM python:3.7-slim-stretch  
  2. RUN pip install flask  
  3. COPY . .  
  4. ENV FLASK_APP=exampleapp:app  
  5. CMD ["flask""run""--host""0.0.0.0"]  
Now the network diagram looks like this:


Takeaways
1. By default, containers run in their own network namespaces, with their own IP addresses.
2. docker run -p 5000:5000 will forward from all interfaces in the main network namespace (or more accurately, the one where the Docker daemon is running) to the external IP in the container.
3. You therefore need to listen on the external IP inside the container, and the easiest way to do that is by listening on all interfaces: 0.0.0.0.


沒有留言:

張貼留言

[Git 常見問題] error: The following untracked working tree files would be overwritten by merge

  Source From  Here 方案1: // x -----删除忽略文件已经对 git 来说不识别的文件 // d -----删除未被添加到 git 的路径中的文件 // f -----强制运行 #   git clean -d -fx 方案2: 今天在服务器上  gi...