แอพ Python ไม่ได้พิมพ์อะไรเลยเมื่อใช้งานตัวแยกออก


160

ฉันมีแอพ Python (2.7) ที่เริ่มต้นใน dockerfile ของฉัน:

CMD ["python","main.py"]

main.pyพิมพ์สตริงบางอย่างเมื่อเริ่มทำงานและเข้าสู่ลูปหลังจากนั้น:

print "App started"
while True:
    time.sleep(1)

ตราบใดที่ฉันเริ่มคอนเทนเนอร์ด้วยแฟล็ก -it ทุกอย่างทำงานได้ตามปกติ:

$ docker run --name=myapp -it myappimage
> App started

และฉันสามารถเห็นผลลัพธ์เดียวกันผ่านบันทึกในภายหลัง:

$ docker logs myapp
> App started

หากฉันพยายามเรียกใช้คอนเทนเนอร์เดียวกันด้วยแฟล็ก -d ดูเหมือนว่าคอนเทนเนอร์จะเริ่มตามปกติ แต่ฉันไม่เห็นผลลัพธ์ใด ๆ :

$ docker run --name=myapp -d myappimage
> b82db1120fee5f92c80000f30f6bdc84e068bafa32738ab7adb47e641b19b4d1
$ docker logs myapp
$ (empty)

แต่ดูเหมือนว่าคอนเทนเนอร์จะยังทำงานอยู่

$ docker ps
Container Status ...
myapp     up 4 minutes ... 

ไฟล์แนบไม่แสดงอะไรเลย:

$ docker attach --sig-proxy=false myapp
(working, no output)

ความคิดใดที่จะผิดพลาด? "พิมพ์" ทำงานแตกต่างกันเมื่อทำงานในพื้นหลังหรือไม่?

รุ่นนักเทียบท่า:

Client version: 1.5.0
Client API version: 1.17
Go version (client): go1.4.2
Git commit (client): a8a31ef
OS/Arch (client): linux/arm
Server version: 1.5.0
Server API version: 1.17
Go version (server): go1.4.2
Git commit (server): a8a31ef

คำตอบ:


265

ในที่สุดผมก็พบวิธีแก้ปัญหาที่จะเห็นการส่งออกงูใหญ่เมื่อทำงาน daemonized ในหางขอบคุณ @ahmetalpbalkan มากกว่าที่GitHub ตอบได้ที่นี่ตัวเองสำหรับการอ้างอิงเพิ่มเติม:

การใช้เอาต์พุตที่ไม่มีบัฟเฟอร์ด้วย

CMD ["python","-u","main.py"]

แทน

CMD ["python","main.py"]

แก้ปัญหา คุณสามารถดูผลลัพธ์ (ทั้ง stderr และ stdout) ผ่านทาง

docker logs myapp

ในขณะนี้!


2
-u ดูเหมือนว่าจะทำงานให้ฉัน แต่มีเอกสารบางอย่างที่มีคำอธิบายของสิ่งที่มันจะทำจริง?
geek น้อย

7
ตามคำแนะนำอื่น ๆ คุณสามารถลองตั้งค่าตัวแปรสภาพแวดล้อมENV PYTHONUNBUFFERED=0ในกรณีที่การ-uตั้งค่าสถานะไม่ทำงาน
Farshid T

1
นี่คือปัญหาของฉันด้วย สำหรับคำอธิบายโดยละเอียดเพิ่มเติมดูstackoverflow.com/a/24183941/562883
Jonathan Stray

มีอะไรเพิ่มเติมเกี่ยวกับ-uที่นี่: stackoverflow.com/questions/107705/disable-output-buffering
cardamom

1
ทำงานเหมือนฝันบน python3 ในขณะที่ตั้ง PYTHONUNBUFFERED = 0 ไม่ได้ช่วยอะไรเลย
Lech Migdal

71

ในกรณีของฉันการใช้งาน Python โดยที่-uไม่ได้เปลี่ยนแปลงอะไรเลย สิ่งที่หลอกลวงได้คือการตั้งค่าPYTHONUNBUFFERED=0เป็นตัวแปรสภาพแวดล้อม:

docker run --name=myapp -e PYTHONUNBUFFERED=0 -d myappimage

6
ในกรณีของฉันการเพิ่มความ-e PYTHONUNBUFFERED=0ช่วยเหลือ
David Ng

1
ขอบคุณ! -uฉันถูกทุบหัวของฉันออกมาจากผนังสำหรับชั่วโมงและไม่สามารถได้รับการบันทึกในการทำงานแม้จะมี โซลูชันของคุณแก้ไขให้ฉันที่ Docker for Mac พร้อม Django
Someguy123

2
ฉันคิดว่านี่เป็นวิธีที่ดีกว่าที่เราไม่ต้องสร้างภาพนักเทียบท่าเพื่อดูผลลัพธ์
FF0605

2
ขอบคุณมากค่ะ มูลค่าการกล่าวขวัญว่านี่จะต้องเป็นตัวละครที่ว่างเปล่าเพื่อทำงานตามเอกสารPYTHONUNBUFFERED
ดาว 10

ทำงานสำหรับส่วนต่อประสานนักเขียน คงไม่เคยเดาได้เลย
deepelement

24

สำหรับฉันมันเป็นคุณสมบัติไม่ใช่ข้อผิดพลาด หากไม่มี pseudo-TTY จะไม่มีสิ่งใดที่จะต้องทำ ดังนั้นวิธีง่ายๆคือการจัดสรรหลอก -TTY สำหรับภาชนะที่ใช้ของคุณด้วย:

$ docker run -t ...

สิ่งนี้ไม่ได้ให้คำตอบสำหรับคำถาม หากต้องการวิจารณ์หรือขอคำชี้แจงจากผู้แต่งโปรดแสดงความคิดเห็นใต้โพสต์ของพวกเขา
ประธานาธิบดี James K. Polk

@JamesKPolk ตอนนี้ดีขึ้นไหม
ปีเตอร์เซนนา

นักเทียบท่าไม่จำเป็นต้องมีหลอก tty ที่จะจัดสรรสำหรับ stdout และ stderr
แมตต์

3
tty: trueในการเขียนแผ่นดิน
องค์ประกอบลึก

15

ดูบทความนี้ซึ่งอธิบายเหตุผลรายละเอียดสำหรับพฤติกรรม:

โดยทั่วไปจะมีสามโหมดสำหรับการบัฟเฟอร์:

  • หากตัวบ่งชี้ไฟล์ถูกยกเลิกการบัฟเฟอร์จะไม่มีการบัฟเฟอร์ใด ๆ เกิดขึ้นและการเรียกใช้ฟังก์ชันที่อ่านหรือเขียนข้อมูลเกิดขึ้นทันที (และจะปิดกั้น)
  • หากตัวบ่งชี้ไฟล์เต็มบัฟเฟอร์บัฟเฟอร์ขนาดคงที่จะถูกใช้และอ่านหรือเขียนการโทรเพียงแค่อ่านหรือเขียนจากบัฟเฟอร์ บัฟเฟอร์ไม่ถูกชะล้างจนกว่าจะเต็ม
  • หาก file descriptor ถูก line-buffered การบัฟเฟอร์จะรอจนกว่าจะเห็นอักขระขึ้นบรรทัดใหม่ ดังนั้นข้อมูลจะบัฟเฟอร์และบัฟเฟอร์จนกว่าจะเห็น \ n จากนั้นข้อมูลทั้งหมดที่บัฟเฟอร์จะถูกล้างข้อมูล ณ เวลานั้น ในความเป็นจริงโดยทั่วไปจะมีขนาดสูงสุดในบัฟเฟอร์ (เช่นเดียวกับในกรณีบัฟเฟอร์เต็ม) ดังนั้นกฎจึงเหมือน "บัฟเฟอร์จนกว่าจะเห็นอักขระขึ้นบรรทัดใหม่หรือพบข้อมูล 4096 ไบต์แล้วแต่ว่าสิ่งใดเกิดขึ้นก่อน"

และ GNU libc (glibc) ใช้กฎต่อไปนี้สำหรับการบัฟเฟอร์:

Stream               Type          Behavior
stdin                input         line-buffered
stdout (TTY)         output        line-buffered
stdout (not a TTY)   output        fully-buffered
stderr               output        unbuffered

ดังนั้นหากใช้-tจากเอกสารนักเทียบท่ามันจะทำการจัดสรรหลอกให้stdoutกลายเป็นline-bufferedดังนั้นจึงdocker run --name=myapp -it myappimageสามารถเห็นเอาต์พุตหนึ่งบรรทัด

และถ้าเพียงใช้ -dไม่มีการจัดสรร tty ดังนั้นstdoutคือfully-bufferedหนึ่งบรรทัดApp startedจะไม่สามารถล้างบัฟเฟอร์ได้

จากนั้นใช้-dtไปmake stdout line bufferedหรือเพิ่ม-uในหลามจะflush the bufferเป็นวิธีที่จะแก้ไขได้


8

หากคุณต้องการเพิ่มเอาต์พุตการพิมพ์ของคุณไปยังเอาต์พุตขวดของคุณเมื่อใช้งานdocker-compose upให้เพิ่มสิ่งต่อไปนี้ในไฟล์เขียนนักเทียบท่าของคุณ

web:
  environment:
    - PYTHONUNBUFFERED=1

https://docs.docker.com/compose/environment-variables/


6

คุณสามารถดูบันทึกรูปภาพที่ถอดออกได้หากคุณเปลี่ยนprintเป็นloggingไป

main.py:

import time
import logging
print "App started"
logging.warning("Log app started")
while True:
    time.sleep(1)

Dockerfile:

FROM python:2.7-stretch
ADD . /app
WORKDIR /app
CMD ["python","main.py"]

1
ดี เคล็ดลับ: ใช้ Python 3
adhg

คำถามอยู่ใน Python 2 (คำสั่งพิมพ์โดยไม่มีวงเล็บ) ดังนั้นใช้ 2 ที่นี่ แม้ว่ามันจะเป็นพฤติกรรมที่เหมือนกันใน Python3.6 ดังนั้นขอขอบคุณสำหรับเคล็ดลับ;)
:

6

เนื่องจากฉันยังไม่เห็นคำตอบนี้:

คุณยังสามารถล้างข้อมูล stdout หลังจากพิมพ์ได้:

import time

if __name__ == '__main__':
    while True:
        print('cleaner is up', flush=True)
        time.sleep(5)

1
มันทำงานได้อย่างสมบูรณ์แบบสำหรับฉันงี่เง่าที่ต้องอยู่ที่นั่น แต่ใช้งานได้ดีในขณะนี้
jamescampbell

5

ลองเพิ่มตัวแปรสภาพแวดล้อมทั้งสองนี้ในโซลูชันของคุณPYTHONUNBUFFERED=1และPYTHONIOENCODING=UTF-8


3

วิธีแก้ไขปัญหาอย่างรวดเร็วลองสิ่งนี้:

from __future__ import print_function
# some code
print("App started", file=sys.stderr)

สิ่งนี้ใช้ได้สำหรับฉันเมื่อฉันพบปัญหาเดียวกัน แต่ตามจริงแล้วฉันไม่รู้ว่าทำไมข้อผิดพลาดนี้จึงเกิดขึ้น


ขอบคุณสำหรับทิป! พยายามแทนที่งานพิมพ์ทั้งหมดด้วยเวอร์ชันของคุณ แต่น่าเสียดายที่มันไม่ได้ผลสำหรับฉันยังคงไม่สามารถรับเอาต์พุตใด ๆ ผ่านบันทึกนักเทียบท่า (การเปลี่ยนระหว่าง sys.stderr / sys.stdout ไม่มีผลลัพธ์ที่มองเห็นได้) นี่เป็นข้อผิดพลาดนักเทียบท่าหรือไม่
jpdus

ดูคำตอบของฉันเหตุผลคือ: stderr ไม่มีข้อผิดพลาดดังนั้นคุณสามารถแก้ไขได้ด้วยโซลูชันของคุณ
atline


0

โดยปกติเราเปลี่ยนเส้นทางไปยังไฟล์ที่ระบุ (โดยติดตั้งไดรฟ์จากโฮสต์และเขียนลงในไฟล์นั้น)

การเพิ่ม tty โดยใช้ -t ก็ใช้ได้เช่นกัน คุณต้องรับมันในบันทึกของนักเทียบท่า

การใช้เอาต์พุตบันทึกขนาดใหญ่ฉันไม่ได้มีปัญหากับบัฟเฟอร์ที่จัดเก็บทั้งหมดโดยไม่ใส่ไว้ในบันทึกของนักเทียบท่า


-1

หากคุณไม่ได้ใช้งานdocker-composeและเป็นเรื่องปกติdockerแทนคุณสามารถเพิ่มสิ่งนี้ลงในDockerfileแอปพลิเคชั่นขวด

ARG FLASK_ENV="production"
ENV FLASK_ENV="${FLASK_ENV}" \
    PYTHONUNBUFFERED="true"

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