Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

initial watchdog #505

Merged
merged 37 commits into from
Feb 9, 2020
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
f5e877f
initial watchdog
cehbrecht Dec 16, 2019
7821562
fix tests
cehbrecht Dec 16, 2019
dba42b4
removed unused finally clause
cehbrecht Dec 18, 2019
b2402c1
simplified processes load
cehbrecht Dec 18, 2019
3a93271
reverted to NullPool
cehbrecht Dec 18, 2019
0e5a0de
removed flufl.enum
cehbrecht Dec 18, 2019
15b759e
pep8
cehbrecht Dec 18, 2019
4ad9b27
renamed watchdog to jobqueue
cehbrecht Dec 18, 2019
869f3dc
fixed jobqueue config
cehbrecht Dec 18, 2019
2c7cbea
updated docs
cehbrecht Dec 18, 2019
c22a0a6
fixed parallelprocesses
cehbrecht Dec 18, 2019
1826c3e
update service description
cehbrecht Dec 18, 2019
e2046b3
using pywps.log
cehbrecht Dec 18, 2019
d5f08d1
only log when queue has changed
cehbrecht Dec 18, 2019
e6d4a28
fix pep8
cehbrecht Dec 18, 2019
e666640
Introduces Alembic database management
jachym Dec 31, 2019
62d6eec
Merge pull request #1 from jachym/alembic
cehbrecht Jan 2, 2020
d73b63c
updated alembic
cehbrecht Jan 2, 2020
a6173fa
fix tests on travis
cehbrecht Jan 2, 2020
97e36f3
fix travis
cehbrecht Jan 2, 2020
086b124
fix travis
cehbrecht Jan 2, 2020
66a61a3
fix travis
cehbrecht Jan 2, 2020
2deb839
fix pep8
cehbrecht Jan 2, 2020
7c38fc7
using pywps -c pywps.cfg
cehbrecht Jan 2, 2020
0c1b97a
fix travis
cehbrecht Jan 2, 2020
e310fff
fix imports
cehbrecht Jan 2, 2020
166ade0
fix imports
cehbrecht Jan 2, 2020
c620c0f
fixed header
cehbrecht Jan 9, 2020
617d600
test python 3.7
cehbrecht Jan 10, 2020
935197d
fix travis for python 3.7
cehbrecht Jan 10, 2020
ffd350c
fix travis
cehbrecht Jan 10, 2020
1d0d3d8
allow overwrite of pywps config
cehbrecht Jan 10, 2020
d42a4f0
fixed typo
cehbrecht Jan 10, 2020
df041a9
fix migrate cli
cehbrecht Jan 10, 2020
8474ce7
fixed log message
cehbrecht Jan 14, 2020
2099b6e
using only default database prefix
cehbrecht Jan 14, 2020
c8ac7dd
added drop_db to migrate
cehbrecht Jan 23, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ dist: trusty

python:
- "3.6"
# - "2.7"

git:
submodules: false
Expand Down Expand Up @@ -37,6 +38,7 @@ script:
- python -m unittest tests
- coverage run --source=pywps -m unittest tests
- flake8 pywps/
- flake8 bin/

after_success:
- coveralls
Expand Down
22 changes: 21 additions & 1 deletion docs/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ The configuration file has several sections:

* `metadata:main` for the server metadata inputs
* `server` for server configuration
* `jobqueue` for job queue configuration
* `processing` for processing backend configuration
* `logging` for logging configuration
* `grass` for *optional* configuration to support `GRASS GIS
Expand Down Expand Up @@ -101,6 +102,12 @@ configuration file <https://docs.pycsw.org/en/latest/configuration.html>`_.
https://docs.python.org/2/library/codecs.html#standard-encodings). Default
value is 'UTF-8'

:processes:
optional parameter to configure the processes list using a Python expression.
This expression will be loaded using ``importlib``.
For example use `myapp.processes.processes` which points to the Python
list of your processes in your application: ``processes = [Sleep(), SayHello()]``.

:parallelprocesses:
maximum number of parallel running processes - set this number carefully.
The effective number of parallel running processes is limited by the number
Expand Down Expand Up @@ -187,6 +194,10 @@ configuration file <https://docs.pycsw.org/en/latest/configuration.html>`_.
Connection string to database where the login about requests/responses is to be stored. We are using `SQLAlchemy <https://docs.sqlalchemy.org/en/latest/core/engines.html#database-urls>`_
please use the configuration string. The default is SQLite3 `:memory:` object, however this has `known issues <https://github.com/geopython/pywps/issues?utf8=%E2%9C%93&q=is%3Aissue+async+sqlite>`_ with async processing and should be avoided.

:db_echo:
flag to enable database logging.

Default = `false`.

[grass]
-------
Expand All @@ -195,8 +206,15 @@ configuration file <https://docs.pycsw.org/en/latest/configuration.html>`_.
directory of the GRASS GIS instalation, refered as `GISBASE
<https://grass.osgeo.org/grass73/manuals/variables.html>`_

[jobqueue]
--------

:pause:
pausing in seconds between periodical check for new stored requests

[s3]
----

:bucket:
Name of the bucket to store files in. e.g. ``my-wps-results``

Expand Down Expand Up @@ -269,10 +287,12 @@ Sample file
[grass]
gisbase=/usr/local/grass-7.3.svn/

[jobqueue]
pause=30

[s3]
bucket=my-org-wps
region=us-east-1
prefix=appname/coolapp/
public=true
encrypt=false

169 changes: 117 additions & 52 deletions docs/deployment.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,61 @@
Deployment to a production server
=================================

PyWPS consists from two main parts:
* PyWPS :py:class:`pywps.app.Service` the main process to accept client requests,
* :py:module:`pywps.queue` responsible for calling the stored requests in the asynchronous mode.

--------------
Service module
--------------
The :py:class:`pywps.app.Service` class is responsible for *synchronous*
request executions: GetCapabilites, DescribeProcess and Execute in sync. mode,
in this case, the Service class will

1. Accept request
2a. In case, the request is to be executed in *synchronous* mode, it will be
directly executed - sync requests are immediately executed.
2b. In case, the request is to be executed as *asynchronous* mode, request
will be stored in to the database.

This means: asynchronous requests are not executed by the Service class, they
will be just stored into database.

------------
Queue module
------------
The :py:module:`pywps.queue` has a `JobQueueService` to be started separately - it will
start a process, which will periodically check for the database stored
requests and in case, some process is there, it will removed and executed.

The service does only start as many processes are allowed in the
`parallelprocesses` process number.

The service does not accept requests from the client, it is just watching the
database and starting process jobs.


------------
Installation
------------
As already described in the :ref:`installation` section, no specific deployment
procedures are for PyWPS when using flask-based server. But this formula is not
procedures are for PyWPS when using flask-based server. But this formula is not
intended to be used in a production environment. For production, `sudo service apache2 restartApache httpd
<https://httpd.apache.org/>`_ or `nginx <https://nginx.org/>`_ servers are
more advised. PyWPS is runs as a `WSGI
<https://wsgi.readthedocs.io/en/latest/>`_ application on those servers. PyWPS
relies on the `Werkzeug <http://werkzeug.pocoo.org/>`_ library for this purpose.

Then the job queue service has to be started too.

Deploying an individual PyWPS instance
--------------------------------------

PyWPS should be installed in your computer (as per the :ref:`installation`
section). As a following step, you can now create several instances of your WPS
PyWPS should be installed in your computer (as per the :ref:`installation`
section). As a following step, you can now create several instances of your WPS
server.

It is advisable for each PyWPS instance to have its own directory, where the
It is advisable for each PyWPS instance to have its own directory, where the
WSGI file along with available processes should reside. Therefore create a new
directory for the PyWPS instance::

Expand All @@ -29,21 +68,40 @@ directory for the PyWPS instance::

.. note:: In this configuration example it is assumed that there is only one
instance of PyWPS on the server.
Each instance is represented by a single `WSGI` script (written in Python),

Each instance is represented by a single `WSGI` script (written in Python),
which:

1. Loads the configuration files
2. Serves processes
3. Takes care about maximum number of concurrent processes and similar

Starting PyWPS using Werkzeug
-----------------------------

PyWPS has a command line interface to start a PyWPS instance with Werkzeug for
development purposes. You need a configuration file ```pywps.cfg`` pointing to
the available processes::

[server]
processes = myapp.processes.processes

Start the pywps with the following command::

$ pywps start -c pywps.cfg

You can also start the pywps service and the job queue as seperated processes::

$ pywps start -c pywps.cfg --no-jobqueue
$ pywps jobqueue -c pywps.cfg

Creating a PyWPS `WSGI` instance
--------------------------------

An example WSGI script is distributed along with the pywps-flask service, as
described in the :ref:`installation` section. The script is actually
straightforward - in fact, it's a just wrapper around the PyWPS server with a
list of processes and configuration files passed as arguments. Here is an
An example WSGI script is distributed along with the pywps-flask service, as
described in the :ref:`installation` section. The script is actually
straightforward - in fact, it's a just wrapper around the PyWPS server with a
list of processes and configuration files passed as arguments. Here is an
example of a PyWPS WSGI script::

$ $EDITOR /path/to/pywps/pywps.wsgi
Expand Down Expand Up @@ -86,9 +144,10 @@ example of a PyWPS WSGI script::
processes at hand that can be directly included. Also it assumes, that
the configuration file already exists - which is not the case yet.

The Configuration is described in next chapter (:ref:`configuration`),
The Configuration is described in next chapter (:ref:`configuration`),
as well as process creation and deployment (:ref:`process`).


Deployment on Apache2 httpd server
----------------------------------

Expand Down Expand Up @@ -118,17 +177,17 @@ You then can edit your site configuration file
The `outputpath` directory need also be accessible from the URL mentioned in `outputurl` configuration.

And of course restart the server::

$ sudo service apache2 restart


Deployment on Nginx-Gunicorn
----------------------------

.. note:: We will use Greenunicorn for pyWPS deployment, since it is a very simple to configurate server.
.. note:: We will use Greenunicorn for pyWPS deployment, since it is a very simple to configurate server.

For difference between WSGI server consult: `WSGI comparison <https://www.digitalocean.com/community/tutorials/a-comparison-of-web-servers-for-python-based-web-applications>`_.

uWSGU is more popular than gunicorn, best documentation is probably to be found at `Readthedocs <https://uwsgi-docs.readthedocs.io/en/latest/WSGIquickstart.html>`_.

We need nginx and gunicorn server::
Expand All @@ -139,31 +198,31 @@ We need nginx and gunicorn server::
It is assumed that PyWPS is installed in your system (if not see: ref:`installation`) and we will use pywps-flask as installation example.

First, cloning the pywps-flask example to the root / (you need to be sudoer or root to run the examples)::

$ cd /
$ git clone https://github.com/geopython/pywps-flask.git

Second, preparing the WSGI script for gunicorn. It is necessary that the
WSGI script located in the pywps-flask service is identified as a python module by gunicorn,
this is done by creating a link with .py extention to the wsgi file::
Second, preparing the WSGI script for gunicorn. It is necessary that the
WSGI script located in the pywps-flask service is identified as a python module by gunicorn,
this is done by creating a link with .py extention to the wsgi file::

$ cd /pywps-flask/wsgi
$ ln -s ./pywps.wsgi ./pywps_app.py
$ ln -s ./pywps.wsgi ./pywps_app.py

Gunicorn can already be tested by setting python path on the command options::
$ gunicorn3 -b 127.0.0.1:8081 --workers $((2*`nproc --all`)) --log-syslog --pythonpath /pywps-flask wsgi.pywps_app:application
The command will start a gunicorn instance on the localhost IP and port 8081, logging to systlog
(/var/log/syslog), using pywps process folder /pywps-flask/processes and loading module wsgi.pywps_app and object/function application for WSGI.

.. note:: Gunicorn uses a prefork model where the master process forks processes (workers)
that willl accept incomming connections. The --workers flag sets the number of processes,
the default values is 1 but the recomended value is 2 or 4 times the number of CPU cores.

Next step is to configure NGINX, by pointing to the WSGI server by changing the location paths of the default
site file but editing file /etc/nginx/sites-enabled as follows:::

$ gunicorn3 -b 127.0.0.1:8081 --workers $((2*`nproc --all`)) --log-syslog --pythonpath /pywps-flask wsgi.pywps_app:application

The command will start a gunicorn instance on the localhost IP and port 8081, logging to systlog
(/var/log/syslog), using pywps process folder /pywps-flask/processes and loading module wsgi.pywps_app and object/function application for WSGI.

.. note:: Gunicorn uses a prefork model where the master process forks processes (workers)
that willl accept incomming connections. The --workers flag sets the number of processes,
the default values is 1 but the recomended value is 2 or 4 times the number of CPU cores.

Next step is to configure NGINX, by pointing to the WSGI server by changing the location paths of the default
site file but editing file /etc/nginx/sites-enabled as follows:::

server {
listen 80 default_server;
listen [::]:80 default_server;
Expand All @@ -185,19 +244,19 @@ site file but editing file /etc/nginx/sites-enabled as follows:::
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://127.0.0.1:8081;
}

}
It is likely that part of the proxy configuration is already set on the file /etc/nginx/proxy.conf.
Of course the necessatyrestart of nginx ::

It is likely that part of the proxy configuration is already set on the file /etc/nginx/proxy.conf.
Of course the necessatyrestart of nginx ::

$ service nginx restart

The service will now be available on the IP of the server or localhost ::

http://localhost/wps?request=GetCapabilities&service=wps
The current gunicorn instance was launched by the user. In a production server it is necessary to set gunicorn as a service

The current gunicorn instance was launched by the user. In a production server it is necessary to set gunicorn as a service

On ubuntu 16.04 the systemcltd system requires a service file that will start the gunicorn3 service. The service file (/lib/systemd/system/gunicorn.service)
has to be configure as follows::
Expand All @@ -214,24 +273,30 @@ has to be configure as follows::
ExecStart=/usr/bin/gunicorn3 -b 127.0.0.1:8081 --preload --workers $WORKERS --log-syslog --pythonpath /pywps-flask wsgi.pywps_app:application
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s TERM $MAINPID

[Install]
WantedBy=multi-user.target

And then enable the service and then reload the systemctl daemon::

$ systemctl enable gunicorn3.service
$ systemctl daemon-reload
$ systemctl restart gunicorn3.service

And to check that everything is ok::

$ systemctl status gunicorn3.service

.. note::

Todo NGIX + uWSGI

------------------
Job queue starting
------------------
The job queue has to be started from command line::

pywps jobqueue --config /path/to/configuration/pywps.cfg

.. _deployment-testing:

Expand All @@ -258,11 +323,11 @@ The result should be an XML-encoded error message.
</ows:Exception>
</ows:ExceptionReport>

The server responded with the :py:class:`pywps.exceptions.MissingParameterValue`
exception, telling us that the parameter `service` was not set. This is
compliant with the OGC WPS standard, since each request mast have at least the
`service` and `request` parameters. We can say for now, that this PyWPS
instance is properly deployed on the server, since it returns proper exception
The server responded with the :py:class:`pywps.exceptions.MissingParameterValue`
exception, telling us that the parameter `service` was not set. This is
compliant with the OGC WPS standard, since each request mast have at least the
`service` and `request` parameters. We can say for now, that this PyWPS
instance is properly deployed on the server, since it returns proper exception
report.

We now have to configure the instance by editing the `pywps.cfg` file and adding
Expand Down
Loading