Heroku์์ ๊ฐ๋จํ Flask ์๋ฒ๋ฅผ ์คํํ๊ณ ์์ต๋๋ค.
web: gunicorn --worker-class eventlet -w 1 app:app --log-file=-
๋ค์ํ ๋ค๋ฅธ ํจํค์ง์์ ํธํ์ฑ์ ์ํด Python 2.7.15๋ฅผ ์ฌ์ฉํ๊ณ ์์ต๋๋ค.
Heroku๊ฐ v. 19.8.1๋ก ์ด๋ํ ์ดํ๋ก ์ค๋์ ๋ถํฐ ์ด ๋ฌธ์ ๊ฐ ์ค๋ณต๋ ๊ฒ ๊ฐ์ต๋๋ค. ์ผ๋ถ ์ด๋ฏธ์ง(๋ช kb์์ ๋ช mb ํฌ๊ธฐ)๋ ๋ก๋๋์ง ์์ต๋๋ค. ๋ง์ ์ด๋ฏธ์ง(๋๋ถ๋ถ ์ ๋๋ฉ์ด์ ์ฉ ์คํ๋ผ์ดํธ ์ํธ)๊ฐ ์๋ ์ฌ์ดํธ๊ฐ ์์ผ๋ฉฐ ๋ฌด์์๋ก ์ ํํ๋ฉด ๋งค๋ฒ ๋ก๋๋์ง ์๊ณ ๊ฐ๊ฐ ๋ค์ ์ค๋ฅ๊ฐ ๋ฐ์ํฉ๋๋ค(์ด๋ฏธ์ง๊ฐ ์ด์ ๋ฒ์ ์์ ์บ์๋ ๊ฒฝ์ฐ ๋ฌธ์ ์์ด ๋ก๋๋จ). :
2018-05-29T09:24:36.216949+00:00 app[web.1]: [2018-05-29 09:24:36 +0000] [10] [ERROR] Socket error processing request.
2018-05-29T09:24:36.216969+00:00 app[web.1]: Traceback (most recent call last):
2018-05-29T09:24:36.216971+00:00 app[web.1]: File "/app/.heroku/python/lib/python2.7/site-packages/gunicorn/workers/async.py", line 66, in handle
2018-05-29T09:24:36.216972+00:00 app[web.1]: six.reraise(*sys.exc_info())
2018-05-29T09:24:36.216974+00:00 app[web.1]: File "/app/.heroku/python/lib/python2.7/site-packages/gunicorn/workers/async.py", line 56, in handle
2018-05-29T09:24:36.216976+00:00 app[web.1]: self.handle_request(listener_name, req, client, addr)
2018-05-29T09:24:36.216978+00:00 app[web.1]: File "/app/.heroku/python/lib/python2.7/site-packages/gunicorn/workers/async.py", line 129, in handle_request
2018-05-29T09:24:36.216980+00:00 app[web.1]: six.reraise(*sys.exc_info())
2018-05-29T09:24:36.216981+00:00 app[web.1]: File "/app/.heroku/python/lib/python2.7/site-packages/gunicorn/workers/async.py", line 112, in handle_request
2018-05-29T09:24:36.216983+00:00 app[web.1]: resp.write_file(respiter)
2018-05-29T09:24:36.216985+00:00 app[web.1]: File "/app/.heroku/python/lib/python2.7/site-packages/gunicorn/http/wsgi.py", line 403, in write_file
2018-05-29T09:24:36.216987+00:00 app[web.1]: if not self.sendfile(respiter):
2018-05-29T09:24:36.216989+00:00 app[web.1]: File "/app/.heroku/python/lib/python2.7/site-packages/gunicorn/http/wsgi.py", line 393, in sendfile
2018-05-29T09:24:36.216990+00:00 app[web.1]: sent += sendfile(sockno, fileno, offset + sent, count)
2018-05-29T09:24:36.216992+00:00 app[web.1]: File "/app/.heroku/python/lib/python2.7/site-packages/gunicorn/http/_sendfile.py", line 66, in sendfile
2018-05-29T09:24:36.216994+00:00 app[web.1]: raise OSError(e, os.strerror(e))
2018-05-29T09:24:36.216996+00:00 app[web.1]: OSError: [Errno 11] Resource temporarily unavailable
๋ค์์ requirements.txt์ ๋ค๋ฅธ ๋ฒ์ ์ ๋๋ค.
Flask==0.12.2
gunicorn==19.8.1
pymongo==3.6.1
flask_socketio==2.9.6
flask_cors==3.0.3
eventlet==0.22.1
gevent==1.2.2
gunicorn์ 19.7.1๋ก ๋ณ๊ฒฝํ๋ฉด ๋ฌธ์ ๊ฐ ํด๊ฒฐ๋๋ ๊ฒ ๊ฐ์ต๋๋ค. 19.8.0์์ ์ง์๋ฉ๋๋ค.
2012๋
์ ์ ์ฌํ ๋ฌธ์ ์ ๋ง์ฐฌ๊ฐ์ง๋ก ์์ฒญ ์๊ฐ ์ด๊ณผ ๋ฌธ์ ๋ ๋ฐ์ํ๋ ์ค๋ฅ๊ฐ ๋งค์ฐ ์ฆ๊ฐ์ ์ด๋ฏ๋ก ์์ฒญ ์๊ฐ ์ด๊ณผ ๋ฌธ์ ๊ฐ ์๋๋๋ค. 19.7.1๋ก ๋กค๋ฐฑํ๋ฉด ๋ฌธ์ ๊ฐ ํด๊ฒฐ๋์์ผ๋ฏ๋ก ์ง๊ธ์ ๊ทธ๋๋ก ์ ์งํ์ง๋ง ์ต์ ๋ฒ์ ์ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข์ต๋๋ค. ์ด๊ฒ์ Heroku ๊ณ ์ ์ ๋ฌธ์ ์ผ ์ ์์ต๋๋ค. ๋๋ ์ง๋ ๋ฌ ์ ๋์์ผ ์์์ฐจ๋ ธ์ง๋ง ๊ทธ๋ค์ด ์ธ์ ๋ฒ์ ์ ๋ณ๊ฒฝํ๋์ง์ ๋ํ ์ ๋ณด๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค.
์ค๋๋ ๊ฐ์ ๋ฌธ์ ๋ก ํ๋ฃจ ์ข ์ผ ์ธ์ ์ต๋๋ค. ๊ทธ๋ฆฌ๊ณ ๋๋ _์๊ฐํด_ ๋ง์นจ๋ด ๊ทธ๊ฒ์ ๊ณ ์ณค์ต๋๋ค. ๋๋ nginx, Flask, gunicorn w/ eventlet ๋ฐ docker๋ฅผ ์ฌ์ฉํ๊ณ ์์ต๋๋ค.
๋ด (๊ด๋ จ) pip freeze
์ถ๋ ฅ:
eventlet==0.23.0
Flask==1.0.2
greenlet==0.4.14
gunicorn==19.9.0
๋ด gunicorn ๋ช
๋ น:
gunicorn -b 0.0.0.0:8000 --workers 1 --worker-class eventlet --log-level=DEBUG myapp.wsgi:app
์ฒซ ๋ฒ์งธ ์ฆ์์ ๋ธ๋ผ์ฐ์ ์์ ERR_CONTENT_LENGTH_MISMATCH
๋ฅผ ๋ฐ์์ํค๋ ๋์ฉ๋ ์ ์ ํ์ผ ๋ก๋์์ต๋๋ค. ๋ถ๋ช
ํ ํฐ ์ ์ JS ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ๋ก๋๋์ง ์์๊ธฐ ๋๋ฌธ์ ์ ํ๋ฆฌ์ผ์ด์
์ด ์ค๋จ๋์์ต๋๋ค.
๋ ๋ฒ์งธ ์ฆ์์ nginx๊ฐ error.log์ ๋ค์์ ๊ธฐ๋กํ๋ ๊ฒ์ด์์ต๋๋ค. upstream prematurely closed connection while reading upstream
๋ง์ง๋ง์ผ๋ก gunicorn ๋ก๊ทธ ํญ๋ชฉ์ผ๋ก ๋ค์ ์ถ์ ํ์ต๋๋ค.
Socket error processing request. - [in /usr/local/lib/python2.7/dist-packages/gunicorn/glogging.py:277]
Traceback (most recent call last):
File "/usr/local/lib/python2.7/dist-packages/gunicorn/workers/base_async.py", line 66, in handle
six.reraise(*sys.exc_info())
File "/usr/local/lib/python2.7/dist-packages/gunicorn/workers/base_async.py", line 56, in handle
self.handle_request(listener_name, req, client, addr)
File "/usr/local/lib/python2.7/dist-packages/gunicorn/workers/base_async.py", line 129, in handle_request
six.reraise(*sys.exc_info())
File "/usr/local/lib/python2.7/dist-packages/gunicorn/workers/base_async.py", line 112, in handle_request
resp.write_file(respiter)
File "/usr/local/lib/python2.7/dist-packages/gunicorn/http/wsgi.py", line 403, in write_file
if not self.sendfile(respiter):
File "/usr/local/lib/python2.7/dist-packages/gunicorn/http/wsgi.py", line 393, in sendfile
sent += sendfile(sockno, fileno, offset + sent, count)
File "/usr/local/lib/python2.7/dist-packages/gunicorn/http/_sendfile.py", line 66, in sendfile
raise OSError(e, os.strerror(e))
OSError: [Errno 11] Resource temporarily unavailable
๋ด ์ต์ข
ํด๊ฒฐ์ฑ
์ --no-sendfile
ํ๋๊ทธ๋ก gunicorn์ ์์ํ๋ ๊ฒ์ด์์ผ๋ฉฐ ๋ฌธ์ ๋ ์ฌ๋ผ์ก์ต๋๋ค. ์์? ์ ๋ชจ๋ฅด๊ฒ ์ด... ํจ๊ณผ๊ฐ ์์ด์ ๋คํ์ด์ผ.
๋ํ ์ธ๊ธํ ๊ฐ์น๊ฐ ์๋ ๊ฒ์ ๋ฌธ์ ํด๊ฒฐ ์ค์ nginx.conf๊ฐ ์ฌ๊ธฐ์ ์๋ ์์ ์ ์ ์ฌํ๋๋ก ์ต์ ์ ๋คํ๋ค๋ ์ ์ ๋๋ค. http://docs.gunicorn.org/en/stable/deploy.html
์ด ๋ฌธ์ ๋ ์ถฉ์กฑํฉ๋๋ค. 19.7.0์ด ์ ์๋ํฉ๋๋ค.
์ด๊ฒ์ ์ฆ์ ๋ฐ์ํฉ๋๊น, ์๋๋ฉด ๊ธด ์๋ต์ด ๋ถ๋ถ์ ์ผ๋ก ์ ์ก๋ ํ์ ๋ฐ์ํฉ๋๊น?
์ ์ ํ์ผ ์์ธ ์ ๊ณต
์ด์ ๋ํ ์ ๋ฐ์ดํธ๊ฐ ์์ต๋๊น? 19.9.0์์ ๋์ผํ ๋ฌธ์ ์ ์ง๋ฉดํ๊ณ ์์ต๋๋ค.
๋ชจ๋ ๊ฒ์ด ์ ์๋ํ๊ณ ๊ฐ์๊ธฐ ๋ฐ์ํ๊ธฐ ์์ํ์ต๋๋ค.
@tilgovi ์ด์ ๊ดํ ์ ๋ณด๊ฐ ํ์ํ๋ฉด ์๋ ค์ฃผ์ญ์์ค. ๊ฐ์๊ธฐ ์ด ๋ฌธ์ ๊ฐ ๋ํ๋๊ธฐ ์์ํ์ต๋๋ค
๋์๊ฒ ๋ฌธ์ ๋ eventlet ์์ ์ ๋๋ฌธ์ด์์ต๋๋ค. ๋๋ eventlet์ ์ ๊ฑฐํ๊ณ ์ด์ ๋ชจ๋ ๊ฒ์ด ์ ์์ ๋๋ค.
๋๋ ๊ฐ์ ๋ฌธ์ ๋ฅผ ๋ง๋๋ค. ๊ทธ๋ฆฌ๊ณ @SaintSimmo ์ ์ ์์ ์ ์๊ฒ ์ ๋ง์ต๋๋ค. ์ด ๋ฌธ์ ๋ ๋์ฉ๋ ํ์ผ ๋ค์ด๋ก๋๋ฅผ ์์ํ ๋ ์ฆ์ ๋ฐ์ํฉ๋๋ค. ํ๋ผ์คํฌ์ ์ด๋ฒคํธ๋ ์ ์ฌ์ฉํ๊ณ ์์ต๋๋ค. ๊ทธ๋ฆฌ๊ณ ๋ค์ด๋ก๋ ์์
์ Flask์ send_from_directory์ ์ํด ์ํ๋ฉ๋๋ค.
gunicorn์ ๋ค์ ๋ช
๋ น์ผ๋ก ์์๋ฉ๋๋ค.
gunicorn --worker-class eventlet -w 1 -b 0.0.0.0:4000 ์
๋ก๋: ์ฑ
์ค๋ฅ๊ฐ ๋ฐ์ํฉ๋๋ค.
๋ช
๋ น์ "--no-sendfile"์ ์ถ๊ฐํ๋ฉด ์ค๋ฅ ๋ฉ์์ง๊ฐ ์ ์ก๋์ง ์์ต๋๋ค. "sendfile" ์์ด ๋ค์ด๋ก๋ ์์
์ ์ํํ ์ ์๋ค๋ฉด ์ธ์ ์ด "sendfile"์ ์ฌ์ฉํด์ผ ํฉ๋๊น?
gunicorn(๋ฒ์ 19.9.0) eventlet๊ณผ ๋์ผํ ๋ฌธ์
19.9.0์์ @SaintSimmo ์ --no-sendfile
์ค์ ๊ถ์ฅ ์ฌํญ ๋ ์ ์๊ฒ ํจ๊ณผ์ ์ด์์ต๋๋ค.
์, ๋์ผํ ๋ฌธ์ ์ด๋ฉฐ eventlet ์์ ์๋ฅผ ์ ๊ฑฐํ ํ ์ ๋๋ก ์๋ํฉ๋๋ค.
์ด ๋ช
๋ น(gunicorn -w 1 -k eventlet -b 127.0.0.1:5000 wsgi:app)์ผ๋ก ์๋ฒ๋ฅผ ์์ํ๋ฉด ์๋ ์์ธ๊ฐ ๋ฐ์ํ๊ณ ํด๋ผ์ด์ธํธ ์๋ต์์ ๋ด ์ด๋ฏธ์ง๊ฐ ์๋ฆฝ๋๋ค.
[์ค๋ฅ] ์์ผ ์ค๋ฅ ์ฒ๋ฆฌ ์์ฒญ.
...
BlockingIOError: [Errno 11] ๋ฆฌ์์ค๋ฅผ ์ผ์์ ์ผ๋ก ์ฌ์ฉํ ์ ์์ต๋๋ค.
์์
์ ํด๋์ค ์ ์๋ฅผ ์ ๊ฑฐํ๋ฉด ์๋ํฉ๋๋ค.
gunicorn -w 1 -b 127.0.0.1:5000 wsgi:app
@jacebrowning ๋ฐ @SaintSimmo , gunicorn ๋ช ๋ น์ --no-sendfile ์ถ๊ฐ ๋งค๊ฐ๋ณ์๊ฐ ํจ๊ณผ์ ์ธ์ง ํ์ธํฉ๋๋ค.
์ด๊ฒ์ ์ผ๋ฐ์ ์ผ๋ก nproc ์ด ๋๋ฌด ์ ๊ธฐ ๋๋ฌธ์ ๋๋ค. ' /etc/security/limits.conf ' ํ์ผ์ ํธ์งํ์ฌ ํด๋น ํ๋ก๊ทธ๋จ์ ์คํํ๋ ์ฌ์ฉ์์ nproc ์๋ฅผ ๋๋ฆด ์ ์์ต๋๋ค.
์ด ๋ฌธ์ ๋ก ๊ณ ํต ๋ฐ๊ณ ์์ต๋๊น? Python 3.4 ์ด์์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ Gunicorn ๋ฒ์ ์ ์ ๋ฐ์ดํธํ์ญ์์ค.
--worker-class์ ๊ฐ์ ๋ฌธ์ ๊ฐ ์์์ต๋๋ค. gunicorn์ 20.0.4๋ก ์ ๋ฐ์ดํธํ๊ณ ๋ฌธ์ ๊ฐ ํด๊ฒฐ๋์์ต๋๋ค.
์์
์ ํด๋์ค๋ฅผ gevent๋ก ๋ณ๊ฒฝํ๋ฉด ์ ์๊ฒ ํจ๊ณผ์ ์ด์์ต๋๋ค. --worker-class gevent
๊ฐ์ฅ ์ ์ฉํ ๋๊ธ
์ค๋๋ ๊ฐ์ ๋ฌธ์ ๋ก ํ๋ฃจ ์ข ์ผ ์ธ์ ์ต๋๋ค. ๊ทธ๋ฆฌ๊ณ ๋๋ _์๊ฐํด_ ๋ง์นจ๋ด ๊ทธ๊ฒ์ ๊ณ ์ณค์ต๋๋ค. ๋๋ nginx, Flask, gunicorn w/ eventlet ๋ฐ docker๋ฅผ ์ฌ์ฉํ๊ณ ์์ต๋๋ค.
๋ด (๊ด๋ จ)
pip freeze
์ถ๋ ฅ:๋ด gunicorn ๋ช ๋ น:
gunicorn -b 0.0.0.0:8000 --workers 1 --worker-class eventlet --log-level=DEBUG myapp.wsgi:app
์ฒซ ๋ฒ์งธ ์ฆ์์ ๋ธ๋ผ์ฐ์ ์์
ERR_CONTENT_LENGTH_MISMATCH
๋ฅผ ๋ฐ์์ํค๋ ๋์ฉ๋ ์ ์ ํ์ผ ๋ก๋์์ต๋๋ค. ๋ถ๋ช ํ ํฐ ์ ์ JS ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ๋ก๋๋์ง ์์๊ธฐ ๋๋ฌธ์ ์ ํ๋ฆฌ์ผ์ด์ ์ด ์ค๋จ๋์์ต๋๋ค.๋ ๋ฒ์งธ ์ฆ์์ nginx๊ฐ error.log์ ๋ค์์ ๊ธฐ๋กํ๋ ๊ฒ์ด์์ต๋๋ค.
upstream prematurely closed connection while reading upstream
๋ง์ง๋ง์ผ๋ก gunicorn ๋ก๊ทธ ํญ๋ชฉ์ผ๋ก ๋ค์ ์ถ์ ํ์ต๋๋ค.
๋ด ์ต์ข ํด๊ฒฐ์ฑ ์
--no-sendfile
ํ๋๊ทธ๋ก gunicorn์ ์์ํ๋ ๊ฒ์ด์์ผ๋ฉฐ ๋ฌธ์ ๋ ์ฌ๋ผ์ก์ต๋๋ค. ์์? ์ ๋ชจ๋ฅด๊ฒ ์ด... ํจ๊ณผ๊ฐ ์์ด์ ๋คํ์ด์ผ.๋ํ ์ธ๊ธํ ๊ฐ์น๊ฐ ์๋ ๊ฒ์ ๋ฌธ์ ํด๊ฒฐ ์ค์ nginx.conf๊ฐ ์ฌ๊ธฐ์ ์๋ ์์ ์ ์ ์ฌํ๋๋ก ์ต์ ์ ๋คํ๋ค๋ ์ ์ ๋๋ค. http://docs.gunicorn.org/en/stable/deploy.html