我正在一个生活在Docker容器内的Flask应用程序中运行joblib以及uWSGI(启用线程启动),它由supervisord启动。
Web服务器的启动显示以下错误:
unable to load configuration from from multiprocessing.semaphore_tracker import main;main(15)
/usr/local/lib/python3.5/dist-packages/sklearn/externals/joblib/_multiprocessing_helpers.py:38: UserWarning:
[Errno 32] Broken pipe. joblib will operate in serial mode
知道如何解决这个问题并使joblib并行运行吗?谢谢!
以下包安装在docker容器中:
pytest==4.0.1
pytest-cov==2.6.0
flake8==3.6.0
Cython==0.29.3
numpy==1.16.1
pandas==0.24.0
scikit-learn==0.20.2
fancyimpute==0.4.2
scikit-garden==0.1.3
category_encoders==1.3.0
boto3==1.9.86
joblib==0.13.1
dash==0.37.0
dash-renderer==0.18.0
dash-core-components==0.43.1
dash-table==3.4.0
dash-html-components==0.13.5
dash-auth==1.3.2
Flask-Caching==1.4.0
plotly==3.6.1
APScheduler==3.5.3
问题是由于uWSGI,nginx或supervisord引起的。缺少dev/shm
的权利不是问题,因为如果我直接运行烧瓶服务器就可以创建信号量。在下面找到三种服务的配置文件。免责声明,我是网络服务器菜鸟,而且这些配置是通过复制和粘贴来自不同的博客而生成的,只是为了让它起作用:-D
所以这是我的uwsgi配置:
[uwsgi]
module = prism_dash_frontend.__main__
callable = server
uid = nginx
gid = nginx
plugins = python3
socket = /tmp/uwsgi.sock
chown-socket = nginx:nginx
chmod-socket = 664
# set cheaper algorithm to use, if not set default will be used
cheaper-algo = spare
# minimum number of workers to keep at all times
cheaper = 3
# number of workers to spawn at startup
cheaper-initial = 5
# maximum number of workers that can be spawned
workers = 5
# how many workers should be spawned at a time
cheaper-step = 1
processes = 5
die-on-term = true
enable-threads = true
nginx配置:
# based on default config of nginx 1.12.1
# Define the user that will own and run the Nginx server
user nginx;
# Define the number of worker processes; recommended value is the number of
# cores that are being used by your server
# auto will default to number of vcpus/cores
worker_processes auto;
# altering default pid file location
pid /tmp/nginx.pid;
# turn off daemon mode to be watched by supervisord
daemon off;
# Enables the use of JIT for regular expressions to speed-up their processing.
pcre_jit on;
# Define the location on the file system of the error log, plus the minimum
# severity to log messages for
error_log /var/log/nginx/error.log warn;
# events block defines the parameters that affect connection processing.
events {
# Define the maximum number of simultaneous connections that can be opened by a worker process
worker_connections 1024;
}
# http block defines the parameters for how NGINX should handle HTTP web traffic
http {
# Include the file defining the list of file types that are supported by NGINX
include /etc/nginx/mime.types;
# Define the default file type that is returned to the user
default_type text/html;
# Don't tell nginx version to clients.
server_tokens off;
# Specifies the maximum accepted body size of a client request, as
# indicated by the request header Content-Length. If the stated content
# length is greater than this size, then the client receives the HTTP
# error code 413. Set to 0 to disable.
client_max_body_size 0;
# Define the format of log messages.
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
# Define the location of the log of access attempts to NGINX
access_log /var/log/nginx/access.log main;
# Define the parameters to optimize the delivery of static content
sendfile on;
tcp_nopush on;
tcp_nodelay on;
# Define the timeout value for keep-alive connections with the client
keepalive_timeout 65;
# Define the usage of the gzip compression algorithm to reduce the amount of data to transmit
#gzip on;
# Include additional parameters for virtual host(s)/server(s)
include /etc/nginx/conf.d/*.conf;
}
supervisord配置:
[supervisord]
nodaemon=true
[program:uwsgi]
command=/usr/bin/uwsgi --ini /etc/uwsgi/uwsgi.ini
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
[program:nginx]
command=/usr/sbin/nginx
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
从Python 3.5迁移到3.7.2后,错误的性质略有改变:
unable to load configuration from from multiprocessing.semaphore_tracker import main;main(15)
/usr/local/lib/python3.7/multiprocessing/semaphore_tracker.py:55: UserWarning:
semaphore_tracker: process died unexpectedly, relaunching. Some semaphores might leak.
unable to load configuration from from multiprocessing.semaphore_tracker import main;main(15)
非常感谢,这对我来说是一个很大的障碍: - /
HERE on my github account是一个最小,完整,可验证的例子。
你可以通过make build
然后make run
轻松运行它。
它将显示以下日志消息:
unable to load configuration from from multiprocessing.semaphore_tracker import main;main(14)
一旦你访问http://127.0.0.1:8080/
并出现以下错误就崩溃了:
exception calling callback for <Future at 0x7fbc520c7eb8 state=finished raised TerminatedWorkerError>
Traceback (most recent call last):
File "/usr/local/lib/python3.7/site-packages/joblib/externals/loky/_base.py", line 625, in _invoke_callbacks
callback(self)
File "/usr/local/lib/python3.7/site-packages/joblib/parallel.py", line 309, in __call__
self.parallel.dispatch_next()
File "/usr/local/lib/python3.7/site-packages/joblib/parallel.py", line 731, in dispatch_next
if not self.dispatch_one_batch(self._original_iterator):
File "/usr/local/lib/python3.7/site-packages/joblib/parallel.py", line 759, in dispatch_one_batch
self._dispatch(tasks)
File "/usr/local/lib/python3.7/site-packages/joblib/parallel.py", line 716, in _dispatch
job = self._backend.apply_async(batch, callback=cb)
File "/usr/local/lib/python3.7/site-packages/joblib/_parallel_backends.py", line 510, in apply_async
future = self._workers.submit(SafeFunction(func))
File "/usr/local/lib/python3.7/site-packages/joblib/externals/loky/reusable_executor.py", line 151, in submit
fn, *args, **kwargs)
File "/usr/local/lib/python3.7/site-packages/joblib/externals/loky/process_executor.py", line 1022, in submit
raise self._flags.broken
joblib.externals.loky.process_executor.TerminatedWorkerError: A worker process managed by the executor was unexpectedly terminated. This could be caused by a segmentation fault while calling the function or by an excessive memory usage causing the Operating System to kill the worker. The exit codes of the workers are {EXIT(1), EXIT(1), EXIT(1), EXIT(1)}
这是一个很大的兔子洞。
在Github上的joblib问题页面有类似的joblib帖子与Uwsgi失败。但大多数都是针对较老的multiprocessing
后端。新的loky
后端应该解决这些问题。
PR后端有multiprocessing
为uwsgi解决了这个问题:
joblib.Parallel(n_jobs=4,backend="multiprocessing")(joblib.delayed(sqrt)(i ** 2) for i in range(10))
但它有时会随机失败,并回到上述PR试图解决的同一问题。
进一步挖掘显示,当前后端loky
默认并行处理(docs)。但是这些进程没有共享内存访问权限,因此需要序列化和排队的通道。这可能是uWSGI失败和gunicorn工作的原因。
所以我尝试切换到线程而不是进程:
joblib.Parallel(n_jobs=4,prefer="threads")(joblib.delayed(sqrt)(i ** 2) for i in range(10))
它的工作原理:)
好吧,我确实找到了问题的答案。它解决了能够在docker中使用supervisor和nginx运行与joblib相关的库的问题。但是,这不是很令人满意。因此,我不接受我自己的答案,但我在这里发布它,以防其他人有同样的问题,需要找到一个好的解决方案。
解决方案是用gunicorn取代uWSGI。好吧,至少我现在知道它的错。我仍然会感谢使用uWSGI安装了gunicorn解决问题的答案。
看来你的图像上没有启用信号量:Joblib检查multiprocessing.Semaphore()
,只有root对/dev/shm
中的共享内存有读/写权限。看看this question和this answer。
这是在我的一个容器中运行的。
$ ls -ld /dev/shm
drwxrwxrwt 2 root root 40 Feb 19 15:23 /dev/shm
如果您以非root身份运行,则应更改/dev/shm
的权限。要设置正确的权限,您需要修改Docker镜像中的/etc/fstab
:
none /dev/shm tmpfs rw,nosuid,nodev,noexec 0 0