การปรับใช้แอพขวดขั้นต่ำในนักเทียบท่า - ปัญหาการเชื่อมต่อเซิร์ฟเวอร์


110

ฉันมี app 5000ที่เป็นเพียงการพึ่งพาเป็นกระติกน้ำซึ่งไหลนักเทียบท่าอยู่ข้างนอกดีและผูกกับพอร์ตเริ่มต้น นี่คือแหล่งที่มาทั้งหมด:

from flask import Flask

app = Flask(__name__)
app.debug = True

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

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

ปัญหาคือเมื่อฉันปรับใช้สิ่งนี้ในนักเทียบท่าเซิร์ฟเวอร์กำลังทำงาน แต่ไม่สามารถเข้าถึงได้จากภายนอกคอนเทนเนอร์

ด้านล่างนี้คือ 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 .

/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 -anหรือคำสั่งใด ๆ ที่คุณจะทำเมื่อคุณดีบัก Flask (หางแมว ... )
user2915097

@ user2915097: ive เพิ่มเอาต์พุตบางส่วนจากภายในเซิร์ฟเวอร์
Dreen

'ไม่สามารถเชื่อมต่อ ... ' @Dreen คุณสามารถเชื่อมต่อได้คุณเพิ่งได้รับคำตอบที่ว่างเปล่า ( Connected to 127.0.0.1)
ForceBru

คำตอบ:


184

ปัญหาคือคุณผูกไว้กับอินเตอร์เฟส localhost เท่านั้นคุณควรผูกมัด0.0.0.0หากคุณต้องการให้คอนเทนเนอร์เข้าถึงได้จากภายนอก หากคุณเปลี่ยน:

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

ถึง

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

มันควรจะทำงาน


วิธีนี้ใช้ได้ผล เราสามารถดูผลลัพธ์โดยใช้สคริปต์ Dockerfile และ python แบบเต็มได้ที่นี่ตามที่โซลูชันนี้อธิบาย

อะไรคือความแตกต่าง?
Jwan622

2
@ Jwan622 อินเทอร์เฟซ localhost พร้อมใช้งานภายในคอนเทนเนอร์เท่านั้น 0.0.0.0 ผูกกับอินเทอร์เฟซทั้งหมด อินเทอร์เฟซเชื่อมต่อกับเครือข่ายต่างๆ (ดังนั้นคุณสามารถมีได้สำหรับ wifi,
lans

อย่าลืมผูกพอร์ต 5000 กับคอนเทนเนอร์ของคุณด้วย-p 5000:5000แฟล็กด้วยdocker runคำสั่งของคุณ
T Loch.

40

เมื่อใช้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 ซึ่ง afaik เป็นวิธีการเริ่มต้นแอป 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" ]ทำงานได้เหมือนแชมป์
โจ

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

เพื่อเข้าถึงแอพ Flask ของคุณที่ทำงานใน Docker container จากเครื่องโฮสต์ของคุณ แทนที่172.17.0.2ด้วยที่อยู่ IP เฉพาะของคอนเทนเนอร์ของคุณ


2

ก่อนอื่นในสคริปต์ python ของคุณคุณต้องเปลี่ยนรหัสจาก

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 จัดเตรียมค่าเริ่มต้นสำหรับการเรียกใช้คอนเทนเนอร์


2

เพื่อสร้างคำตอบอื่น ๆ :

ลองนึกภาพคุณมีคอมพิวเตอร์สองเครื่อง คอมพิวเตอร์แต่ละเครื่องมีอินเทอร์เฟซเครือข่าย (WiFi, พูด) ซึ่งเป็น IP สาธารณะ คอมพิวเตอร์แต่ละเครื่องมีอินเทอร์เฟซ loopback / localhost ที่ 127.0.0.1 ซึ่งหมายความว่า "คอมพิวเตอร์เครื่องนี้เท่านั้น"

หากคุณจดทะเบียนใน 127.0.0.1 บนคอมพิวเตอร์ของคุณจะไม่ได้คาดหวังว่าจะสามารถเชื่อมต่อกับที่ผ่าน 127.0.0.1 เมื่อทำงานบนคอมพิวเตอร์ B. หลังจากที่ทุกท่านขอให้ฟังบนคอมพิวเตอร์ของท้องถิ่นที่อยู่ส่วนตัว

Docker เป็นการตั้งค่าที่คล้ายกัน ในทางเทคนิคแล้วมันเป็นคอมพิวเตอร์เครื่องเดียวกัน แต่เคอร์เนลของ Linux อนุญาตให้แต่ละคอนเทนเนอร์ทำงานด้วยสแต็กเครือข่ายแยกของตัวเอง 127.0.0.1 ในคอนเทนเนอร์จึงเหมือนกับ 127.0.0.1 บนคอมพิวเตอร์เครื่องอื่นที่ไม่ใช่โฮสต์ของคุณคุณไม่สามารถเชื่อมต่อกับมันได้

เวอร์ชันที่ยาวขึ้นพร้อมไดอะแกรม: https://pythonspeed.com/articles/docker-connection-refused/

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.