Add WSGI support for `cloudkitty-api'

Recommands to setup cloudkitty through an other WSGI services
like Apache 'mod_wsgi'. And the community has set a community wide goal
in Pike cycle: "Control Plane API endpoints deployment via WSGI"
https://governance.openstack.org/tc/goals/pike/deploy-api-in-wsgi.html

Work Item: Add WSGI support
1. Provide WSGI application script file.
2. Removing the cloudkitty-api command line.
3. Adding cloudkitty-api wsgi_scripts, by 'cloudkitty-api -p 8889' to run.

Work Item: Make the devstack setup ck-api with wsgi
1. Switch devstack jobs to deploy control-plane API services in WSGI with Apache.
2. Default to deploy with Apache by global ENABLE_HTTPD_MOD_WSGI_SERVICES, in
   local.conf expose CLOUDKITTY_USE_MOD_WSGI=False to run without Apache.

Work Item: Update the docs about installation
1. Installing the cloudkitty-api behind mod_wsgi.
2. Updating the installation about the cloudkitty-api.

Implements: blueprint wsgi-support
Change-Id: I207587c5360bb80c0e856cd0239e4073578951aa
This commit is contained in:
zhangguoqing 2016-09-06 18:14:22 +08:00 committed by Luka Peschke
parent efb35ee3a0
commit 2dc80310ba
10 changed files with 208 additions and 64 deletions

View File

@ -15,9 +15,7 @@
#
# @author: Stéphane Albert
#
import logging
import os
from wsgiref import simple_server
from oslo_config import cfg
from oslo_log import log
@ -26,6 +24,7 @@ import pecan
from cloudkitty.api import config as api_config
from cloudkitty.api import hooks
from cloudkitty import service
from cloudkitty import storage
@ -34,8 +33,7 @@ LOG = log.getLogger(__name__)
auth_opts = [
cfg.StrOpt('api_paste_config',
default="api_paste.ini",
help="Configuration file for WSGI definition of API."
),
help="Configuration file for WSGI definition of API."),
cfg.StrOpt('auth_strategy',
choices=['noauth', 'keystone'],
default='keystone',
@ -45,11 +43,11 @@ auth_opts = [
api_opts = [
cfg.IPOpt('host_ip',
default="0.0.0.0",
help='Host serving the API.'),
default='0.0.0.0',
help='The listen IP for the cloudkitty API server.'),
cfg.PortOpt('port',
default=8889,
help='Host port serving the API.'),
help='The port for the cloudkitty API server.'),
cfg.BoolOpt('pecan_debug',
default=False,
help='Toggle Pecan Debug Middleware.'),
@ -103,35 +101,9 @@ def load_app():
return deploy.loadapp("config:" + cfg_file)
def build_server():
# Create the WSGI server and start it
host = CONF.api.host_ip
port = CONF.api.port
LOG.info('Starting server in PID %s', os.getpid())
LOG.info("Configuration:")
cfg.CONF.log_opt_values(LOG, logging.INFO)
if host == '0.0.0.0':
LOG.info('serving on 0.0.0.0:%(sport)s, view at \
http://127.0.0.1:%(vport)s',
{'sport': port, 'vport': port})
else:
LOG.info("serving on http://%(host)s:%(port)s",
{'host': host, 'port': port})
server_cls = simple_server.WSGIServer
handler_cls = simple_server.WSGIRequestHandler
app = load_app()
srv = simple_server.make_server(
host,
port,
app,
server_cls,
handler_cls)
return srv
def build_wsgi_app(argv=None):
service.prepare_service([])
return load_app()
def app_factory(global_config, **local_conf):

View File

@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
# Copyright 2014 Objectif Libre
# -*- mode: python -*-
#
# Copyright 2013 New Dream Network, LLC (DreamHost)
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
@ -12,21 +13,12 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Stéphane Albert
#
"""Use this file for deploying the API under mod_wsgi.
See http://pecan.readthedocs.org/en/latest/deployment.html for details.
"""
from cloudkitty.api import app
from cloudkitty import service
def main():
service.prepare_service()
server = app.build_server()
try:
server.serve_forever()
except KeyboardInterrupt:
pass
if __name__ == '__main__':
main()
application = app.build_wsgi_app(argv=[])

View File

@ -0,0 +1,15 @@
Listen %PORT%
<VirtualHost *:%PORT%>
WSGIDaemonProcess cloudkitty-api processes=2 threads=10 user=%USER% display-name=%{GROUP} %VIRTUALENV%
WSGIProcessGroup cloudkitty-api
WSGIScriptAlias / %WSGIAPP%
WSGIApplicationGroup %{GLOBAL}
<IfVersion >= 2.4>
ErrorLogFormat "%{cu}t %M"
</IfVersion>
ErrorLog /var/log/%APACHE_NAME%/cloudkitty.log
CustomLog /var/log/%APACHE_NAME%/cloudkitty_access.log combined
</VirtualHost>
WSGISocketPrefix /var/run/%APACHE_NAME%

View File

@ -73,9 +73,19 @@ function is_cloudkitty_enabled {
return 1
}
# Remove WSGI files, disable and remove Apache vhost file
function _cloudkitty_cleanup_apache_wsgi {
if is_service_enabled ck-api && [ "$CLOUDKITTY_USE_MOD_WSGI" == "True" ]; then
sudo rm -f "$CLOUDKITTY_WSGI_DIR"/*
sudo rm -rf "$CLOUDKITTY_WSGI_DIR"
sudo rm -f $(apache_site_config_for cloudkitty)
fi
}
# cleanup_cloudkitty() - Remove residual data files, anything left over from previous
# runs that a clean run would need to clean up
function cleanup_cloudkitty {
_cloudkitty_cleanup_apache_wsgi
# Clean up dirs
rm -rf $CLOUDKITTY_AUTH_CACHE_DIR/*
rm -rf $CLOUDKITTY_CONF_DIR/*
@ -85,6 +95,31 @@ function cleanup_cloudkitty {
done
}
# Configure mod_wsgi
function _cloudkitty_config_apache_wsgi {
sudo mkdir -m 755 -p $CLOUDKITTY_WSGI_DIR
local cloudkitty_apache_conf=$(apache_site_config_for cloudkitty)
local venv_path=""
# Copy proxy vhost and wsgi file
sudo cp $CLOUDKITTY_DIR/cloudkitty/api/app.wsgi $CLOUDKITTY_WSGI_DIR/app.wsgi
if [[ ${USE_VENV} = True ]]; then
venv_path="python-path=${PROJECT_VENV["cloudkitty"]}/lib/$(python_version)/site-packages"
fi
sudo cp $CLOUDKITTY_DIR/devstack/apache-cloudkitty.template $cloudkitty_apache_conf
sudo sed -e "
s|%PORT%|$CLOUDKITTY_SERVICE_PORT|g;
s|%APACHE_NAME%|$APACHE_NAME|g;
s|%WSGIAPP%|$CLOUDKITTY_WSGI_DIR/app.wsgi|g;
s|%USER%|$STACK_USER|g;
s|%VIRTUALENV%|$venv_path|g
" -i $cloudkitty_apache_conf
}
# configure_cloudkitty() - Set config files, create data dirs, etc
function configure_cloudkitty {
setup_develop $CLOUDKITTY_DIR
@ -137,6 +172,10 @@ function configure_cloudkitty {
# keystone middleware
configure_auth_token_middleware $CLOUDKITTY_CONF cloudkitty $CLOUDKITTY_AUTH_CACHE_DIR
if is_service_enabled ck-api && [ "$CLOUDKITTY_USE_MOD_WSGI" == "True" ]; then
_cloudkitty_config_apache_wsgi
fi
}
# create_cloudkitty_cache_dir() - Part of the init_cloudkitty() process
@ -195,7 +234,14 @@ function install_cloudkitty {
# start_cloudkitty() - Start running processes, including screen
function start_cloudkitty {
run_process ck-proc "$CLOUDKITTY_BIN_DIR/cloudkitty-processor --config-file=$CLOUDKITTY_CONF"
run_process ck-api "$CLOUDKITTY_BIN_DIR/cloudkitty-api --config-file=$CLOUDKITTY_CONF"
if [[ "$CLOUDKITTY_USE_MOD_WSGI" == "False" ]]; then
run_process ck-api "$CLOUDKITTY_BIN_DIR/cloudkitty-api --config-file=$CLOUDKITTY_CONF"
elif is_service_enabled ck-api; then
enable_apache_site cloudkitty
restart_apache_server
tail_log cloudkitty /var/log/$APACHE_NAME/cloudkitty.log
tail_log cloudkitty-api /var/log/$APACHE_NAME/cloudkitty_access.log
fi
echo "Waiting for ck-api ($CLOUDKITTY_SERVICE_HOST:$CLOUDKITTY_SERVICE_PORT) to start..."
if ! wait_for_service $SERVICE_TIMEOUT $CLOUDKITTY_SERVICE_PROTOCOL://$CLOUDKITTY_SERVICE_HOST:$CLOUDKITTY_SERVICE_PORT; then
die $LINENO "ck-api did not start"
@ -205,9 +251,19 @@ function start_cloudkitty {
# stop_cloudkitty() - Stop running processes
function stop_cloudkitty {
# Kill the cloudkitty screen windows
for serv in ck-api ck-proc; do
stop_process $serv
done
if is_service_enabled ck-proc ; then
stop_process ck-proc
fi
if is_service_enabled ck-api ; then
if [ "$CLOUDKITTY_USE_MOD_WSGI" == "True" ]; then
disable_apache_site cloudkitty
restart_apache_server
else
# Kill the cloudkitty screen windows
stop_process ck-api
fi
fi
}
# install_python_cloudkittyclient() - Collect source and prepare

View File

@ -10,12 +10,12 @@ CLOUDKITTY_DIR=$DEST/cloudkitty
CLOUDKITTY_CONF_DIR=/etc/cloudkitty
CLOUDKITTY_CONF=$CLOUDKITTY_CONF_DIR/cloudkitty.conf
CLOUDKITTY_API_LOG_DIR=/var/log/cloudkitty
CLOUDKITTY_WSGI_DIR=${CLOUDKITTY_WSGI_DIR:-/var/www/cloudkitty}
CLOUDKITTY_AUTH_CACHE_DIR=${CLOUDKITTY_AUTH_CACHE_DIR:-/var/cache/cloudkitty}
CLOUDKITTY_DATA_DIR=${CLOUDKITTY_DATA_DIR:-/var/lib/cloudkitty}
CLOUDKITTY_REPORTS_DIR=${DATA_DIR}/cloudkitty/reports
# Horizon enabled file
CLOUDKITTY_DASHBOARD=$DEST/cloudkitty-dashboard/cloudkittydashboard
CLOUDKITTY_ENABLED_DIR=${CLOUDKITTY_ENABLED_DIR:-${CLOUDKITTY_DASHBOARD}/enabled}
CLOUDKITTY_HORIZON_ENABLED_DIR=${CLOUDKITTY_HORIZON_ENABLED_DIR:-$HORIZON_DIR/openstack_dashboard/enabled}
@ -32,6 +32,7 @@ CLOUDKITTY_SERVICE_HOST=${CLOUDKITTY_SERVICE_HOST:-$SERVICE_HOST}
CLOUDKITTY_SERVICE_PORT=${CLOUDKITTY_SERVICE_PORT:-8889}
CLOUDKITTY_SERVICE_HOSTPORT="$CLOUDKITTY_SERVICE_HOST:$CLOUDKITTY_SERVICE_PORT"
CLOUDKITTY_SERVICE_PROTOCOL=${CLOUDKITTY_SERVICE_PROTOCOL:-$SERVICE_PROTOCOL}
CLOUDKITTY_USE_MOD_WSGI=${CLOUDKITTY_USE_MOD_WSGI:-${ENABLE_HTTPD_MOD_WSGI_SERVICES}}
# Set CloudKitty auth info
CLOUDKITTY_PRICING_USER=${CLOUDKITTY_PRICING_USER:-"admin"}

View File

@ -21,6 +21,7 @@ Installation
devstack
installation
mod_wsgi
Architecture

View File

@ -327,17 +327,30 @@ Start cloudkitty
If you installed cloudkitty from packages
-----------------------------------------
Start the API and processing services::
Start the processing services::
systemctl start cloudkitty-api.service
systemctl start cloudkitty-processor.service
If you installed cloudkitty from sources
-----------------------------------------
Start the API and processing services::
Start the processing services::
cloudkitty-api --config-file /etc/cloudkitty/cloudkitty.conf
cloudkitty-processor --config-file /etc/cloudkitty/cloudkitty.conf
Choose and start the API server
-------------------------------
Cloudkitty includes the ``cloudkitty-api`` command. It can be
used to run the API server. For smaller or proof-of-concept
installations this is a reasonable choice. For larger installations it
is strongly recommended to install the API server in a WSGI host
such as mod_wsgi (see :doc:`mod_wsgi`). Doing so will provide better
performance and more options for making adjustments specific to the
installation environment.
If you are using the ``cloudkitty-api`` command it can be started
as::
$ cloudkitty-api -p 8889

53
doc/source/mod_wsgi.rst Normal file
View File

@ -0,0 +1,53 @@
..
Copyright 2013 New Dream Network, LLC (DreamHost)
Licensed under the Apache License, Version 2.0 (the "License"); you may
not use this file except in compliance with the License. You may obtain
a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations
under the License.
===================================
Installing the API behind mod_wsgi
===================================
Cloudkitty comes with a few example files for configuring the API
service to run behind Apache with ``mod_wsgi``.
app.wsgi
========
The file ``cloudkitty/api/app.wsgi`` sets up the V1 API WSGI
application. The file needs to be copied to /var/www/cloudkitty/,
and should not need to be modified.
etc/apache2/cloudkitty
======================
The ``etc/apache2/cloudkitty`` file contains example settings that
work with a copy of cloudkitty installed via devstack.
.. literalinclude:: ../../etc/apache2/cloudkitty
1. On deb-based systems copy or symlink the file to
``/etc/apache2/sites-available``. For rpm-based systems the file will go in
``/etc/httpd/conf.d``.
2. Modify the ``WSGIDaemonProcess`` directive to set the ``user`` and
``group`` values to an appropriate user on your server. In many
installations ``cloudkitty`` will be correct.
3. Enable the cloudkitty site. On deb-based systems::
$ a2ensite cloudkitty
$ service apache2 reload
On rpm-based systems::
$ service httpd reload

39
etc/apache2/cloudkitty Normal file
View File

@ -0,0 +1,39 @@
# Copyright (c) 2013 New Dream Network, LLC (DreamHost)
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
# This is an example Apache2 configuration file for using the
# cloudkitty API through mod_wsgi.
# Note: If you are using a Debian-based system then the paths
# "/var/log/httpd" and "/var/run/httpd" will use "apache2" instead
# of "httpd".
#
# The number of processes and threads is an example only and should
# be adjusted according to local requirements.
Listen 8889
<VirtualHost *:8889>
WSGIDaemonProcess cloudkitty-api processes=2 threads=10 user=SOMEUSER display-name=%{GROUP}
WSGIProcessGroup cloudkitty-api
WSGIScriptAlias / /var/www/cloudkitty/app.wsgi
WSGIApplicationGroup %{GLOBAL}
<IfVersion >= 2.4>
ErrorLogFormat "%{cu}t %M"
</IfVersion>
ErrorLog /var/log/httpd/cloudkitty_error.log
CustomLog /var/log/httpd/cloudkitty_access.log combined
</VirtualHost>
WSGISocketPrefix /var/run/httpd

View File

@ -24,12 +24,14 @@ packages =
[entry_points]
console_scripts =
cloudkitty-api = cloudkitty.cli.api:main
cloudkitty-dbsync = cloudkitty.cli.dbsync:main
cloudkitty-processor = cloudkitty.cli.processor:main
cloudkitty-storage-init = cloudkitty.cli.storage:main
cloudkitty-writer = cloudkitty.cli.writer:main
wsgi_scripts =
cloudkitty-api = cloudkitty.api.app:build_wsgi_app
oslo.config.opts =
cloudkitty.common.config = cloudkitty.common.config:list_opts