在Docker中部署最小化Flask应用-服务器连接问题

问题:在Docker中部署最小化Flask应用-服务器连接问题

我有一个唯一依赖的应用程序是flask,它可以在docker外部正常运行并绑定到默认端口5000。这是完整的源代码:

from flask import Flask

app = Flask(__name__)
app.debug = True

@app.route('/')
def main():
    return 'hi'

if __name__ == '__main__':
    app.run()

问题是,当我在docker中部署此服务器时,服务器正在运行,但无法从容器外部访问。

以下是我的Dockerfile。该图像是装有烧瓶的ubuntu。焦油仅包含index.py上面列出的内容;

# Dockerfile
FROM dreen/flask
MAINTAINER dreen
WORKDIR /srv

# Get source
RUN mkdir -p /srv
COPY perfektimprezy.tar.gz /srv/perfektimprezy.tar.gz
RUN tar x -f perfektimprezy.tar.gz
RUN rm perfektimprezy.tar.gz

# Run server
EXPOSE 5000
CMD ["python", "index.py"]

这是我正在部署的步骤

$> sudo docker build -t perfektimprezy .

据我所知,上面的代码运行良好,图像中包含tar的内容/srv。现在,让我们在容器中启动服务器:

$> sudo docker run -i -p 5000:5000 -d perfektimprezy
1c50b67d45b1a4feade72276394811c8399b1b95692e0914ee72b103ff54c769

它真的在运行吗?

$> sudo docker ps
CONTAINER ID        IMAGE                   COMMAND             CREATED             STATUS              PORTS                    NAMES
1c50b67d45b1        perfektimprezy:latest   "python index.py"   5 seconds ago       Up 5 seconds        0.0.0.0:5000->5000/tcp   loving_wozniak

$> sudo docker logs 1c50b67d45b1
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
 * Restarting with stat

是的,好像flask服务器正在运行。这是奇怪的地方。让我们向服务器发出请求:

 $> curl 127.0.0.1:5000 -v
 * Rebuilt URL to: 127.0.0.1:5000/
 * Hostname was NOT found in DNS cache
 *   Trying 127.0.0.1...
 * Connected to 127.0.0.1 (127.0.0.1) port 5000 (#0)
 > GET / HTTP/1.1
 > User-Agent: curl/7.35.0
 > Host: 127.0.0.1:5000
 > Accept: */*
 >
 * Empty reply from server
 * Connection #0 to host 127.0.0.1 left intact
 curl: (52) Empty reply from server

空回复…但是该流程正在运行吗?

$> sudo docker top 1c50b67d45b1
UID                 PID                 PPID                C                   STIME               TTY                 TIME                CMD
root                2084                812                 0                   10:26               ?                   00:00:00            python index.py
root                2117                2084                0                   10:26               ?                   00:00:00            /usr/bin/python index.py

现在,让我们进入服务器并检查…

$> sudo docker exec -it 1c50b67d45b1 bash
root@1c50b67d45b1:/srv# netstat -an
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State
tcp        0      0 127.0.0.1:5000          0.0.0.0:*               LISTEN
tcp        0      0 127.0.0.1:47677         127.0.0.1:5000          TIME_WAIT
Active UNIX domain sockets (servers and established)
Proto RefCnt Flags       Type       State         I-Node   Path
root@1c50b67d45b1:/srv# curl -I 127.0.0.1:5000
HTTP/1.0 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 5447
Server: Werkzeug/0.10.4 Python/2.7.6
Date: Tue, 19 May 2015 12:18:14 GMT

很好…但是不是从外面来的:(我做错了什么?

I have an app who’s only dependency is flask, which runs fine outside docker and binds to the default port 5000. Here is the full source:

from flask import Flask

app = Flask(__name__)
app.debug = True

@app.route('/')
def main():
    return 'hi'

if __name__ == '__main__':
    app.run()

The problem is that when I deploy this in docker, the server is running but is unreachable from outside the container.

Below is my Dockerfile. The image is ubuntu with flask installed. The tar just contains the index.py listed above;

# Dockerfile
FROM dreen/flask
MAINTAINER dreen
WORKDIR /srv

# Get source
RUN mkdir -p /srv
COPY perfektimprezy.tar.gz /srv/perfektimprezy.tar.gz
RUN tar x -f perfektimprezy.tar.gz
RUN rm perfektimprezy.tar.gz

# Run server
EXPOSE 5000
CMD ["python", "index.py"]

Here are the steps I am doing to deploy

$> sudo docker build -t perfektimprezy .

As far as I know the above runs fine, the image has the contents of the tar in /srv. Now, let’s start the server in a container:

$> sudo docker run -i -p 5000:5000 -d perfektimprezy
1c50b67d45b1a4feade72276394811c8399b1b95692e0914ee72b103ff54c769

Is it actually running?

$> sudo docker ps
CONTAINER ID        IMAGE                   COMMAND             CREATED             STATUS              PORTS                    NAMES
1c50b67d45b1        perfektimprezy:latest   "python index.py"   5 seconds ago       Up 5 seconds        0.0.0.0:5000->5000/tcp   loving_wozniak

$> sudo docker logs 1c50b67d45b1
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
 * Restarting with stat

Yep, seems like the flask server is running. Here is where it gets weird. Lets make a request to the server:

 $> curl 127.0.0.1:5000 -v
 * Rebuilt URL to: 127.0.0.1:5000/
 * Hostname was NOT found in DNS cache
 *   Trying 127.0.0.1...
 * Connected to 127.0.0.1 (127.0.0.1) port 5000 (#0)
 > GET / HTTP/1.1
 > User-Agent: curl/7.35.0
 > Host: 127.0.0.1:5000
 > Accept: */*
 >
 * Empty reply from server
 * Connection #0 to host 127.0.0.1 left intact
 curl: (52) Empty reply from server

Empty reply… But is the process running?

$> sudo docker top 1c50b67d45b1
UID                 PID                 PPID                C                   STIME               TTY                 TIME                CMD
root                2084                812                 0                   10:26               ?                   00:00:00            python index.py
root                2117                2084                0                   10:26               ?                   00:00:00            /usr/bin/python index.py

Now let’s ssh into the server and check…

$> sudo docker exec -it 1c50b67d45b1 bash
root@1c50b67d45b1:/srv# netstat -an
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State
tcp        0      0 127.0.0.1:5000          0.0.0.0:*               LISTEN
tcp        0      0 127.0.0.1:47677         127.0.0.1:5000          TIME_WAIT
Active UNIX domain sockets (servers and established)
Proto RefCnt Flags       Type       State         I-Node   Path
root@1c50b67d45b1:/srv# curl -I 127.0.0.1:5000
HTTP/1.0 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 5447
Server: Werkzeug/0.10.4 Python/2.7.6
Date: Tue, 19 May 2015 12:18:14 GMT

It’s fine… but not from the outside :( What am I doing wrong?


回答 0

问题是您只绑定到localhost接口,0.0.0.0如果要从外部访问容器,则应该绑定到。如果您更改:

if __name__ == '__main__':
    app.run()

if __name__ == '__main__':
    app.run(host='0.0.0.0')

它应该工作。

The problem is you are only binding to the localhost interface, you should be binding to 0.0.0.0 if you want the container to be accessible from outside. If you change:

if __name__ == '__main__':
    app.run()

to

if __name__ == '__main__':
    app.run(host='0.0.0.0')

It should work.


回答 1

当使用flask命令代替时app.run,您可以传递--host选项来更改主机。Docker中的行将是:

CMD ["flask", "run", "--host", "0.0.0.0"]

要么

CMD flask run --host 0.0.0.0

When using the flask command instead of app.run, you can pass the --host option to change the host. The line in Docker would be:

CMD ["flask", "run", "--host", "0.0.0.0"]

or

CMD flask run --host 0.0.0.0

回答 2

您的Docker容器具有多个网络接口。例如,我的容器具有以下内容:

$ ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
32: eth0@if33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever

如果运行docker network inspect bridge,您可以在上面的输出中看到容器通过第二个接口连接到该网桥。该默认网桥还连接到主机上的Docker进程。

因此,您将必须运行以下命令:

CMD flask run --host 172.17.0.2

从主机访问在Docker容器中运行的Flask应用。用172.17.0.2您的容器的特定IP地址替换。

Your Docker container has more than one network interface. For example, my container has the following:

$ ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
32: eth0@if33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever

if you run docker network inspect bridge, you can see that your container is connected to that bridge with the second interface in the above output. This default bridge is also connected to the Docker process on your host.

Therefore you would have to run the command:

CMD flask run --host 172.17.0.2

To access your Flask app running in a Docker container from your host machine. Replace 172.17.0.2 with whatever the particular IP address is of your container.


回答 3

以其他答案为基础:

假设您有两台计算机。每台计算机都有一个网络接口(例如WiFi),这是它的公共IP。每台计算机都有一个回送/本地主机接口,位于127.0.0.1。这意味着“仅这台计算机”。

如果在计算机A上列出了127.0.0.1,则在计算机B上运行时,您将无法通过127.0.0.1进行连接。毕竟,您要求侦听计算机A的本地专用地址。

Docker的设置与此类似。从技术上讲,它是同一台计算机,但是Linux内核允许每个容器使用其自己的隔离网络堆栈运行。因此,容器中的127.0.0.1与主机以外的其他计算机上的127.0.0.1相同-您无法连接到它。

较长的版本,带有图表:https : //pythonspeed.com/articles/docker-connection-refused/

To build on other answers:

Imagine you have two computers. Each computer has a network interface (WiFi, say), which is its public IP. Each computer has a loopback/localhost interface, at 127.0.0.1. This means “just this computer.”

If you listed on 127.0.0.1 on computer A, you would not expect to be able to connect to that via 127.0.0.1 when running on computer B. After all, you asked to listen on computer A’s local, private address.

Docker is similar setup; technically it’s the same computer, but the Linux kernel is allowing each container to run with its own isolated network stack. So 127.0.0.1 in a container is the same as 127.0.0.1 on a different computer than your host—you can’t connect to it.

Longer version, with diagrams: https://pythonspeed.com/articles/docker-connection-refused/


回答 4

首先,您需要在python脚本中更改以下代码:

app.run()

app.run(host="0.0.0.0")

其次,在您的docker文件中,最后一行应类似于

CMD ["flask", "run", "-h", "0.0.0.0", "-p", "5000"]

而在主机上,如果0.0.0.0:5000不起作用,那么您应该尝试localhost:5000

注-CMD命令必须正确。因为CMD命令提供了执行容器的默认值。

First of all in your python script you need to change code from

app.run()

to

app.run(host="0.0.0.0")

Second, In your docker file, last line should be like

CMD ["flask", "run", "-h", "0.0.0.0", "-p", "5000"]

And on host machine if 0.0.0.0:5000 doesn’t work then you should try with localhost:5000

Note – The CMD command has to be proper. Because CMD command provide defaults for executing container.