Aplikasi Python tidak mencetak apa pun saat menjalankan detached in docker

160

Saya memiliki aplikasi Python (2.7) yang dimulai di dockerfile saya:

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

main.py mencetak beberapa string ketika dimulai dan masuk ke loop setelahnya:

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

Selama saya memulai wadah dengan flag -it, semuanya berfungsi seperti yang diharapkan:

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

Dan saya bisa melihat output yang sama via log nanti:

$ docker logs myapp
> App started

Jika saya mencoba menjalankan wadah yang sama dengan flag -d, wadah tersebut tampaknya mulai dengan normal, tetapi saya tidak dapat melihat output apa pun:

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

Tapi wadah itu tampaknya masih berjalan;

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

Lampirkan juga tidak menampilkan apa pun:

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

Ada ide apa yang salah? Apakah "cetak" berperilaku berbeda ketika dijalankan di latar belakang?

Versi buruh pelabuhan:

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
jpdus
sumber

Jawaban:

266

Akhirnya saya menemukan solusi untuk melihat output Python ketika menjalankan daemonized di Docker, terima kasih kepada @ahmetalpbalkan di GitHub . Menjawab sendiri di sini untuk referensi lebih lanjut:

Menggunakan keluaran unbuffered dengan

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

dari pada

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

memecahkan masalah; Anda dapat melihat output (keduanya, stderr dan stdout) via

docker logs myapp

sekarang!

jpdus
sumber
2
-u tampaknya bekerja untuk saya, tetapi apakah ada beberapa dokumentasi di suatu tempat dengan deskripsi tentang apa yang sebenarnya dilakukannya?
Little geek
7
Seperti yang disarankan oleh jawaban lain, Anda dapat mencoba mengatur variabel lingkungan ENV PYTHONUNBUFFERED=0jika -uflag tidak berfungsi.
Farshid T
1
Ini juga masalah saya. Untuk penjelasan lebih rinci, lihat stackoverflow.com/a/24183941/562883
Jonathan Stray
Beberapa lebih lanjut tentang di -usini: stackoverflow.com/questions/107705/disable-output-buffering
cardamom
1
Bekerja seperti mimpi di python3, sementara pengaturan PYTHONUNBUFFERED = 0 tidak membantu.
Lech Migdal
71

Dalam kasus saya, menjalankan Python dengan -utidak mengubah apa pun. Apa triknya adalah menetapkan PYTHONUNBUFFERED=0sebagai variabel lingkungan:

docker run --name=myapp -e PYTHONUNBUFFERED=0 -d myappimage
Pemenang
sumber
6
Dalam kasus saya, menambahkan -e PYTHONUNBUFFERED=0bantuan.
David Ng
1
Terima kasih! Saya membenturkan kepala saya ke dinding selama berjam-jam, dan tidak bisa mendapatkan kayu untuk bekerja sekalipun -u. Solusi Anda memperbaikinya bagi saya di Docker untuk Mac dengan Django
Someguy123
2
saya pikir ini adalah solusi yang lebih baik, bahwa kita tidak perlu membangun kembali gambar buruh pelabuhan untuk melihat hasilnya
FF0605
2
Terima kasih banyak. Layak disebutkan bahwa ini hanya perlu menjadi karakter yang tidak kosong untuk bekerja menurut dokumen PYTHONUNBUFFERED
A Star
Bekerja untuk antarmuka docker-compose. Tidak akan pernah bisa menebak
deepelement
24

Bagi saya itu adalah fitur, bukan bug. Tanpa pseudo-TTY tidak ada yang bisa dilakukan. Jadi solusi sederhana adalah dengan mengalokasikan pseudo-TTY untuk wadah Anda berjalan dengan:

$ docker run -t ...
Peter Senna
sumber
Ini tidak memberikan jawaban untuk pertanyaan itu. Untuk mengkritik atau meminta klarifikasi dari penulis, tinggalkan komentar di bawah posting mereka.
Presiden James K. Polk
@ JamesKPolk, apakah sekarang lebih baik?
Peter Senna
buruh pelabuhan tidak memerlukan tty semu untuk dialokasikan untuk stdout dan stderr
Matt
3
tty: truedi tanah komposisi
kedalaman
15

Lihat artikel ini yang menjelaskan alasan detail perilaku tersebut:

Biasanya ada tiga mode untuk buffering:

  • Jika deskriptor file tidak ditemukan maka buffering tidak terjadi apa pun, dan panggilan fungsi yang membaca atau menulis data terjadi segera (dan akan memblokir).
  • Jika file deskriptor sepenuhnya buffered maka buffer ukuran tetap digunakan, dan membaca atau menulis panggilan cukup membaca atau menulis dari buffer. Buffer tidak memerah sampai terisi.
  • Jika deskriptor file buffer baris maka buffer menunggu hingga melihat karakter baris baru. Jadi data akan buffer dan buffer sampai a \ n terlihat, dan kemudian semua data yang buffered memerah pada saat itu. Pada kenyataannya ada biasanya ukuran maksimum pada buffer (seperti dalam kasus buffer penuh), sehingga aturan sebenarnya lebih seperti "buffer sampai karakter baris baru terlihat atau 4096 byte data ditemui, mana yang terjadi terlebih dahulu".

Dan GNU libc (glibc) menggunakan aturan berikut untuk buffering:

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

Jadi, jika digunakan -t, dari dokumen buruh pelabuhan , itu akan mengalokasikan pseudo-tty, kemudian stdoutmenjadi line-buffered, sehingga docker run --name=myapp -it myappimagebisa melihat output satu baris.

Dan, jika hanya digunakan -d, tidak ada tty dialokasikan, maka, stdoutadalah fully-buffered, satu baris App startedpasti tidak dapat menyiram buffer.

Kemudian, gunakan -dtuntuk make stdout line bufferedatau add -udi python untuk flush the bufferadalah cara untuk memperbaikinya.

atline
sumber
6

Anda dapat melihat log pada gambar terpisah jika Anda mengubah printke 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"]
Hog
sumber
1
bagus. tip: gunakan Python 3.
adhg
pertanyaannya adalah dalam Python 2 (pernyataan cetak tanpa tanda kurung) karena itu saya menggunakan 2 di sini. Meskipun perilaku yang persis sama pada Python3.6 jadi terima kasih atas tip;)
The Hog
6

Karena saya belum melihat jawaban ini:

Anda juga dapat menghapus stdout setelah Anda mencetaknya:

import time

if __name__ == '__main__':
    while True:
        print('cleaner is up', flush=True)
        time.sleep(5)
tycl
sumber
1
ini bekerja dengan baik untuk saya, bodoh karena ini perlu ada di sana, tetapi bekerja dengan baik sekarang.
jamescampbell
5

Coba tambahkan dua variabel lingkungan ini ke solusi Anda PYTHONUNBUFFERED=1danPYTHONIOENCODING=UTF-8

Lukasz Dynowski
sumber
3

Sebagai perbaikan cepat, coba ini:

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

Ini bekerja untuk saya ketika saya menemukan masalah yang sama. Tapi, jujur ​​saja, saya tidak tahu mengapa kesalahan ini terjadi.

Vitaly Isaev
sumber
Terima kasih atas tipnya! Sudah mencoba mengganti semua cetakan dengan versi Anda, sayangnya itu tidak bekerja untuk saya, masih tidak bisa mendapatkan output melalui log buruh pelabuhan (mengubah antara sys.stderr / sys.stdout tidak memiliki hasil yang terlihat). Apakah ini bug buruh pelabuhan?
jpdus
Lihat jawaban saya , alasannya adalah: stderr tidak dibuat-buat, sehingga Anda dapat memperbaikinya dengan solusi Anda.
atline
1

Saya harus menggunakan PYTHONUNBUFFERED=1file docker-compose.yml saya untuk melihat output dari RunJver Django.

Adam Radestock
sumber
0

Biasanya, kami mengarahkannya ke file tertentu (dengan memasang volume dari host dan menulisnya ke file itu).

Menambahkan tty menggunakan -t juga baik-baik saja. Anda harus mengambilnya di log buruh pelabuhan.

Menggunakan keluaran log yang besar, saya tidak memiliki masalah dengan penyangga menyimpan semua tanpa memasukkannya ke dalam log buruh pelabuhan.

Edward Aung
sumber
-1

Jika Anda tidak menggunakan docker-composedan hanya normal docker, Anda dapat menambahkan ini ke Anda Dockerfileyang menjadi tuan rumah aplikasi labu

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

CMD [ "flask", "run" ]
G Wayne
sumber