Docker에 최소 플라스크 앱 배포-서버 연결 문제


108

나는 유일한 의존성 인 앱이 플라스크이며, 이는 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입니다. 이미지는 플라스크가 설치된 우분투입니다. tar에는 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 .

위의 내용이 잘 실행되는 한 이미지에는 /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

네, 플라스크 서버가 실행중인 것 같습니다. 여기가 이상해집니다. 서버에 요청할 수 있습니다.

 $> 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

이제 서버에 ssh하고 확인하겠습니다.

$> 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

괜찮아요 ...하지만 밖에서가 아닙니다 :( 내가 뭘 잘못하고 있니?


관련 항목은 "<class 'httplib.BadStatusLine'>에 의해 발생"입니다. stackoverflow.com/questions/16592568/…
user2915097

나는 한 번만 연결하려고 시도하고 있으며 이것이 httpie의 버그가 아니라고 확신합니다 (예제를 curl로 변경했습니다). 서버에서도 도커 외부에서 잘 작동합니다. 나는 이것이 도커 구성 / 배포 실수 문제라는 강한 느낌을 가지고 있습니다.
Dreen

컨테이너를 체크인 docker exec -it 1c50b67d45b1 bash한 다음 netstat -anFlask를 디버그 할 때 수행 할 일반적인 명령 또는 명령 (tail, cat ...)
user2915097

@ user2915097 : ive가 서버 내에서 일부 출력을 추가했습니다
Dreen

'연결할 수 없습니다 ...'@Dreen, 연결할 수 있습니다 . 빈 답장을 Connected to 127.0.0.1
받습니다

답변:


179

문제는 로컬 호스트 인터페이스에만 바인딩한다는 것입니다 0.0.0.0. 컨테이너를 외부에서 액세스 할 수 있도록하려면 바인딩해야합니다 . 변경하는 경우 :

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

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

작동합니다.


이 솔루션이 작동합니다. 이 솔루션이 설명하는대로 여기 에서 전체 Dockerfile 및 python 스크립트를 사용하여 결과를 볼 수 있습니다 .

차이점이 뭐야?
Jwan622

1
@ Jwan622 localhost 인터페이스는 컨테이너 내에서만 사용할 수 있습니다. 0.0.0.0은 모든 인터페이스에 바인딩됩니다. (당신이 무선 랜, 무선 랜 등을 하나 가질 수 있도록) 인터페이스는 다양한 네트워크에 연결
아드리안 Mouat

명령 -p 5000:5000과 함께 플래그를 사용하여 포트 5000을 컨테이너에 바인딩해야합니다 docker run.
T Loch.

39

flask대신 명령을 사용하는 경우 호스트를 변경하는 옵션을 app.run전달할 수 있습니다 --host. Docker의 줄은 다음과 같습니다.

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

또는

CMD flask run --host 0.0.0.0

2
이 솔루션에 대해 대단히 감사합니다. 저도 같은 문제가 있습니다. 왜 app.run(host="0.0.0.0") 작동하지 않는지 아십니까? 나는 또한이 질문에 대한 게시물을 만들었습니다. stackoverflow.com/q/53133350/3279996
xirururu

1
이 메모에서 python run.py --host=0.0.0.0 . 내 명명 규칙으로 인해 가끔씩 나를 얻습니다. 해당 코드는 작동하는 것처럼 보이지만 서버는 로컬 호스트에서 실행됩니다.
Braden Holt

1
프로덕션 환경을위한 것이 아닌 내장 플라스크 서버를 실행 하지 않습니까?
code_dredd

어떤 이유로 든 거의 모든 Flask-with-Docker 정보가 Flask CLI를 사용하지 못합니다. 즉, 앱을 시작하는 Flask 1.0 방법입니다.
Zach Valenta

3
2020 년에 Flask 1.1.1이 app.run(host="0.0.0.0")실패하고 CMD ["flask", "run", "--host", "0.0.0.0" ]챔피언처럼 작동합니다.
Joe

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 주소로 바꿉니다 .


2

다른 답변을 구축하려면 :

두 대의 컴퓨터가 있다고 상상해보십시오. 각 컴퓨터에는 공용 IP 인 네트워크 인터페이스 (예 : WiFi)가 있습니다. 각 컴퓨터에는 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/


0

우선 파이썬 스크립트에서 코드를 변경해야합니다.

app.run()

app.run(host="0.0.0.0")

둘째, 도커 파일에서 마지막 줄은 다음과 같아야합니다.

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

그리고 호스트 컴퓨터에서 작동 0.0.0.0:5000하지 않으면 시도해야합니다.localhost:5000

주-CMD 명령이 적절해야합니다. CMD 명령은 컨테이너 실행에 대한 기본값을 제공하기 때문입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.