Triển khai một ứng dụng bình tối thiểu trong docker - Sự cố kết nối máy chủ


107

Tôi có một ứng dụng phụ thuộc duy nhất là flask, chạy tốt bên ngoài docker và liên kết với cổng mặc định 5000. Đây là nguồn đầy đủ:

from flask import Flask

app = Flask(__name__)
app.debug = True

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

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

Vấn đề là khi tôi triển khai điều này trong docker, máy chủ đang chạy nhưng không thể truy cập được từ bên ngoài vùng chứa.

Dưới đây là Dockerfile của tôi. Hình ảnh là ubuntu có cài đặt flask. Tar chỉ chứa index.pydanh sách ở trên;

# 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"]

Đây là các bước tôi đang làm để triển khai

$> sudo docker build -t perfektimprezy .

Theo như tôi biết ở trên chạy tốt, hình ảnh có nội dung của tar trong /srv. Bây giờ, hãy khởi động máy chủ trong một vùng chứa:

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

Nó có thực sự đang chạy không?

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

Đúng, có vẻ như máy chủ bình đang chạy. Đây là nơi nó trở nên kỳ lạ. Hãy đưa ra yêu cầu tới máy chủ:

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

Trả lời trống ... Nhưng quá trình này có đang chạy không?

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

Bây giờ chúng ta hãy ssh vào máy chủ và kiểm tra ...

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

Nó ổn ... nhưng không phải từ bên ngoài :( Tôi đang làm gì sai?


điều có liên quan là "Do <class 'httplib.BadStatusLine'> gây ra", xem stackoverflow.com/questions/16592568/…
user2915097

Tuy nhiên, tôi chỉ cố gắng kết nối một lần và tôi khá chắc chắn đây không phải là lỗi trong httpie (tôi đã thay đổi ví dụ thành curl ngay bây giờ), cũng không phải trong máy chủ vì nó hoạt động tốt bên ngoài docker. Tôi có cảm giác mạnh rằng đây là vấn đề sai cấu hình / triển khai
docker

Kiểm tra vùng chứa bằng docker exec -it 1c50b67d45b1 bashvà sau đó chọn lệnh thông thường netstat -anhoặc bất kỳ lệnh nào bạn sẽ làm khi gỡ lỗi Bình (đuôi, mèo ...)
user2915097 19/05

@ user2915097: ive đã thêm một số đầu ra từ bên trong máy chủ
Dreen

'Không thể kết nối ...' @Dreen, bạn có thể kết nối , bạn chỉ nhận được một câu trả lời trống ( Connected to 127.0.0.1)
ForceBru

Câu trả lời:


179

Vấn đề là bạn chỉ ràng buộc với giao diện localhost, bạn nên ràng buộc 0.0.0.0nếu bạn muốn vùng chứa có thể truy cập từ bên ngoài. Nếu bạn thay đổi:

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

đến

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

Nó sẽ hoạt động.


Giải pháp này hoạt động. Người ta có thể xem kết quả bằng cách sử dụng Dockerfile và tập lệnh python đầy đủ ở đây như giải pháp này mô tả.

có gì khác biệt?
Jwan622

1
@ Jwan622 Giao diện localhost chỉ khả dụng bên trong vùng chứa. 0.0.0.0 liên kết với tất cả các giao diện. Giao diện kết nối với các mạng khác nhau (vì vậy bạn có thể có một cho wifi, LAN vv)
Adrian Mouat

Đảm bảo rằng bạn nhớ liên kết cổng 5000 với vùng chứa bằng -p 5000:5000cờ bằng docker runlệnh của bạn .
T Loch.

39

Khi sử dụng flasklệnh thay vì app.run, bạn có thể chuyển --hosttùy chọn để thay đổi máy chủ. Dòng trong Docker sẽ là:

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

hoặc là

CMD flask run --host 0.0.0.0

2
Cảm ơn rất nhiều về giải pháp này, tôi cũng có vấn đề tương tự, bạn có biết lý do tại sao app.run(host="0.0.0.0") không hoạt động là gì không? Tôi cũng đã tạo một bài đăng cho câu hỏi này: stackoverflow.com/q/53133350/3279996
xirururu

1
Trên lưu ý này, đảm bảo không sử dụng python run.py --host=0.0.0.0 . Điều đó khiến tôi thỉnh thoảng có được do quy ước đặt tên của tôi. Mã đó sẽ hoạt động nhưng máy chủ sẽ chạy trên máy chủ cục bộ.
Braden Holt

Điều này không chạy máy chủ bình tích hợp, không dành cho môi trường sản xuất?
code_dredd

Điều này thật tuyệt, vì bất cứ lý do gì mà gần như tất cả thông tin Flask-with-Docker không sử dụng Flask CLI, afaik là cách Flask 1.0 để bắt đầu một ứng dụng.
Zach Valenta

3
Đây là năm 2020 với Flask 1.1.1 app.run(host="0.0.0.0")thất bại và hoạt CMD ["flask", "run", "--host", "0.0.0.0" ]động như một nhà vô địch.
Joe

1

Vùng chứa Docker của bạn có nhiều giao diện mạng. Ví dụ: vùng chứa của tôi có những thứ sau:

$ 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

nếu bạn chạy docker network inspect bridge, bạn có thể thấy rằng vùng chứa của bạn được kết nối với cây cầu đó bằng giao diện thứ hai trong đầu ra ở trên. Cầu nối mặc định này cũng được kết nối với quy trình Docker trên máy chủ của bạn.

Do đó, bạn sẽ phải chạy lệnh:

CMD flask run --host 172.17.0.2

Để truy cập ứng dụng Flask của bạn đang chạy trong vùng chứa Docker từ máy chủ của bạn. Thay thế 172.17.0.2bằng bất kỳ địa chỉ IP cụ thể nào trong vùng chứa của bạn.


1

Để xây dựng các câu trả lời khác:

Hãy tưởng tượng bạn có hai máy tính. Mỗi máy tính có một giao diện mạng (chẳng hạn như WiFi), là IP công cộng của nó. Mỗi máy tính có giao diện loopback / localhost, tại 127.0.0.1. Điều này có nghĩa là "chỉ máy tính này."

Nếu bạn liệt kê trên 127.0.0.1 trên máy tính A, bạn sẽ không thể kết nối với nó thông qua 127.0.0.1 khi chạy trên máy tính B. Sau cùng, bạn đã yêu cầu nghe trên địa chỉ riêng, cục bộ của máy tính A.

Docker là thiết lập tương tự; Về mặt kỹ thuật, nó là cùng một máy tính, nhưng nhân Linux cho phép mỗi vùng chứa chạy với ngăn xếp mạng riêng biệt của nó. Vì vậy, 127.0.0.1 trong vùng chứa giống với 127.0.0.1 trên một máy tính khác với máy chủ lưu trữ của bạn — bạn không thể kết nối với nó.

Phiên bản dài hơn, với sơ đồ: https://pythonspeed.com/articles/docker-connection-refused/


0

Trước hết, trong tập lệnh python của bạn, bạn cần thay đổi mã từ

app.run()

đến

app.run(host="0.0.0.0")

Thứ hai, trong tệp docker của bạn, dòng cuối cùng sẽ giống như

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

Và trên máy chủ nếu 0.0.0.0:5000không hoạt động thì bạn nên thử vớilocalhost:5000

Lưu ý - Lệnh CMD phải phù hợp. Vì lệnh CMD cung cấp các giá trị mặc định để thực thi vùng chứa.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.