Introduction
Scenario
In this tutorial, we will look at how one could leverage the multi-tenancy feature from Kill Bill. The scenario we will describe, is one where we would like to provide 'Subscription As A Service' (SaaS) in the cloud (AWS). The tutorial will skip all the operational good practices such as redundancy of instances, firewalls, load balancers and focus instead on the Kill Bill specifics. Also, note that when deploying in the cloud where instances can come and go, the configuration of the bus and notifications queues need to be understood.
We will assume that we only need one plugin to make the payments, and as an example we will use the Stripe plugin.
Users and Roles
Let’s start by defining some users and roles:
-
The user setting up the
Subscription As A Service
in the cloud, is called superadmin, and will have all the privileges to configure the tenants and setup the roles associated with the other users. -
The admin user associated to each tenant will be called
admin_X
whereX
is the name of the tenant.
Let’s take the case of a small yoga studio that wants to use our Subscription As A Service
to provide subscriptions to its users: The name of the merchant is Shakti, and so we will create a user called admin_shakti for the tenant shakti
.
The admin_shakti
user will have full privileges for its Shakti tenant and will not see any of the other tenants.
Of course, for our own purpose of providing the service, we will also create a very first tenant to invoice our merchants (e.g the Shakti business). That tenant will be called saas
('Subscription As A Service'), and the associated admin user will be called admin_saas.
Also, for sake of simplicity, we will setup Kill Bill to use a static Shiro configuration file to configure the various admin users/roles. In real life, it would be more practical to rely on LDAP rather than static configuration, or use the Database configuration approach to avoid having to restart Kill Bill when creating new users/roles. The LDAP configuration is supported by Shiro but we will not describe the steps in that tutorial to keep it simple.
Kill Bill Multi-tenant Deployment
Overview of AWS Deployment Steps
In order to install Kill Bill on AWS you can use our docker images and rely on the aws
from docker-machine
. Alternatively you can setup things by hand, for instance by following the following steps.
The important piece is that=, the port 8080 should be publicly available for Kill Bill (in that simple setup with no firewall) and the port 3000 should be publicly available for KAUI.
Notes:
-
We are using the version
VERSION
for killbill and kaui below to avoid having to update the documentation each time we release. We do suggest you use the latest version as shown in the download page. -
The deployment will consist in separate instances:
-
One instance for killbill, where we will deploy the docker image
killbill/killbill:latest
-
One instance to configure the admin UI (KAUI) where we will deploy the deocker image
killbill/kaui:latest
-
Finally RDS instance
-
For the Kill Bill Deployment, the killbill
database schema will need to include:
-
Kill Bill DDL can be found here: http://docs.killbill.io/VERSION/ddl.sql
So, for each of those schema, you could install the schema from the EC2 zone with the following command:
killbill_ec2> curl -q http://...ddl | mysql -h RDS_URL -u RDS_USER -pRDS_PWD killbill
For the KAUI deployment, the kaui
database will need to include:
Configuration Files
Since we want to realy on the static shiro.ini
file, we need to make sure such a file exists and is available. Fortunately, there is config variable, KILLBILL_SHIRO_RESOURCE_PATH
, that we can pass to our docker image, to take of that.
The shiro.ini
file will look like the following:
[users]
superadmin = PWD, root
admin_shakti = PWD, root_tenant
admin_saas = PWD, root_tenant
[roles]
root = *:*
root_tenant = account,catalog,custom_field,entitlement,invoice,overdue,payment,payment_method,tag,usage
Starting the Kill Bill Instance
Again, here we will be reusing the same instances we configured in the http://killbill.io/tutorials/aws/ [aws tutorial], and start first pull and and then run the docker containers in these EC2 instances:
killbill_ec2> sudo docker pull killbill/killbill:VERSION
killbill_ec2> docker run -tid \
--name killbillsaas \
-p 8080:8080 \
-e KILLBILL_PLUGIN_STRIPE=1 \
-e KILLBILL_PLUGIN_ANALYTICS=1 \
-e KILLBILL_SHIRO_RESOURCE_PATH=/home/ubuntu/killbill/etc/shiro.ini \
-e KILLBILL_CONFIG_DAO_URL=jdbc:mysql://RDS_URL/killbill \
-e KILLBILL_CONFIG_DAO_USER=RDS_USER \
-e KILLBILL_CONFIG_DAO_PASSWORD=RDS_PWD \
-e KILLBILL_CONFIG_OSGI_DAO_URL=jdbc:mysql://RDS_URL/killbill \
-e KILLBILL_CONFIG_OSGI_DAO_USER=RDS_USER \
-e KILLBILL_CONFIG_OSGI_DAO_PASSWORD=RDS_PWD \
killbill/killbill:VERSION
killbill_ec2> sudo docker logs -f killbillsaas // check for instance to be up and running
Finally, since we run the analytics plugin, we need to configure the analytics tables. There is a script that can be run to configure the analytics tables with all the existing views and reports. The script needs to be run from the analytics repo, and it will both hit some endpoints on the running instance of killbill and also create some views through mysql client. If your RDS instance is not visible to the public world, you have two options
-
Clone the repo on the killbill ec2 zone and run the script from there (but that might require installing git, …)
-
Clone the repo on your local machine and create a tunnel (this is the option we will highlight below):
# Create Tunnel through our publicly visible EC2 instance to be able to access the RDS instance
laptop> ssh -i ~/<yourkey>.pem ubuntu@KILLBILL_IP -L13306:RDS_URL:3306 -N
laptop> git clone https://github.com/killbill/killbill-analytics-plugin.git
laptop> cd src/main/resources
laptop> export KILLBILL_HOST=KILLBILL_PUBLIC_IP; export KILLBILL_USER=superadmin; export KILLBILL_PASSWORD=PWD; export MYSQL_HOST=RDS_IP; export MYSQL_HOST=RDS_PORT; export MYSQL_PASSWORD=RDS_PWD ; export MYSQL_USER=RDS_USER; /bin/bash ./seed_reports.sh
If you look in your RDS instance you should see reports configured in the analytics_reports
table and all the views v_report_*
such as v_report_accounts_summary
should exist.
Starting the KAUI Instance
kaui_ec2> sudo docker pull killbill/kaui:VERSION
kaui_ec2> docker run -tid \
--name kaui-saas \
-p 3000:8080 \
-e KAUI_API_KEY= \
-e KAUI_API_SECRET= \
-e KAUI_CONFIG_DAO_URL=jdbc:mysql://RDS_URL/kaui \
-e KAUI_CONFIG_DAO_USER=RDS_USER \
-e KAUI_CONFIG_DAO_PASSWORD=RDS_PWD \
-e KAUI_URL=http://KILLBILL_IP:8080 \
killbill/kaui:VERSION
kaui_ec2> sudo docker logs -f kaui-saas
Saas Setup
Creating the tenants and configuring allowed users
KAUI has been enhanced with new admin screens
, that are described in the Multi-tenancy screens section of that documentation.
The first step is to login as superadmin
to have the rights to create new tenants and configure all allowed users.
Starting on the /admin_tenants
screen, click to Configure a New Tenant
to create the 2 tenants saas
and shakti
; for e.g for shakti
we would enter:
-
Name :
shakti
-
API Key:
some_key_fort_shakti
-
API Secret:
some_secret_for_shakti
-
Click on the
Create tenant
to also create the tenant in Kill Bill.
At this point, the tenant exists in Kill Bill and is known from KAUI as well.
We can then configure the allowed users. KAUI needs to know who can access which tenant, and this information is kept in the KAUI database.
It really means that any user known from Kill Bill (shiro.ini) will be able to make API calls against any tenant provided the user specifies the correct tenant api_key
and api_secret
, so the security resides behind keeping those keys secret.
On the screen /admin_allowed_users
, click on Add a new Allowed User
; for e.g for the shakti administrator we would enter:
-
Name :
admin_shakti
# This has to match theshiro.ini
configuration -
Description : Admin user for tenant
shakti
Then you will be prompted to select the tenant this users has access to. In our example of admin_shakti
, we will select the available tenant shakti
from the list that we previously configured.
Obviously for the user superadmin
we would add the two tenants saas
and shakti
.
When all the users and tenants have been configured, you can try to logout, and login as a specific user (for e.g admin_shakti
).
If the user has only access to one tenant, the process of login-in will directly assign that tenant and all subsequent operations will be made against that tenant.
If the user has more than one tenant, the user will be prompted to chose which tenant to use right after the login screen.
Configuring each tenant
Both Kill Bill and KAUI have been improved to now support uploading per tenant configuration:
-
The UI offers new screens to upload all these new configs
-
The plugins get notified when such config occurs so they can take action if needed
-
In multi-node scenario, there is a mechanism to make sure all nodes will be notified and refresh their view
The following per-tenant configuration can now be uploaded:
-
Per Tenant Versioned Catalog: Each new upload will create a new version of the catalog
-
Per Tenant Overdue Config: Each new upload will overwrite the previous version of the overdue.xml associated with this tenant
-
Per Tenant Invoice Template: Each new upload will overwrite the previous version of the invoice template associated with this tenant
-
Per Tenant Invoice Translation: Each new upload will overwrite the previous version of the invoice translation associated with this tenant
-
Per Tenant Catalog Translation: Each new upload will overwrite the previous version of the catalog translation associated with this tenant
-
Per Tenant Plugin Translation: Each new upload will overwrite the previous version of the config associated with this tenant and this specific plugin
Let’s do some basic configuration for the tenant 'shakti'. We will upload a catalog and then a specific configuration for the stripe plugin. You can login as superadmin
or admin_shakti
since both these users have the right to access that tenant. From the screen /admin_tenants/
chose the shakti tenant.
Then, let’s start with the stripe plugin: Create a valid config and then use the Plugin Config
section of the page to specify the plugin name killbill-stripe
and then upload the yml shown below:
:stripe: :api_secret_key: 'YOUR_VALID_TENANT_API_SECRET_KEY' :api_publishable_key: 'YOUR_VALID_TENANT_API_PUBLISHABLE_KEY'
Then let’s now upload a catalog for our tenant: Create the following catalog and then use the Tenant Catalog XML
section to upload the file associated with the tenant.
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <catalog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="CatalogSchema.xsd "> <effectiveDate>2013-02-08T00:00:00+00:00</effectiveDate> <catalogName>Shakti</catalogName> <recurringBillingMode>IN_ADVANCE</recurringBillingMode> <currencies> <currency>USD</currency> <currency>EUR</currency> </currencies> <products> <product name="Ashtanga"> <category>BASE</category> <included> <addonProduct>Pranayama</addonProduct> </included> </product> <product name="Flow"> <category>BASE</category> </product> <product name="Iyengar"> <category>BASE</category> <available> <addonProduct>Pranayama</addonProduct> </available> </product> <product name="Pranayama"> <category>ADD_ON</category> </product> </products> <rules> <changePolicy> <changePolicyCase> <policy>IMMEDIATE</policy> </changePolicyCase> </changePolicy> <changeAlignment> <changeAlignmentCase> <alignment>START_OF_BUNDLE</alignment> </changeAlignmentCase> </changeAlignment> <cancelPolicy> <cancelPolicyCase> <policy>IMMEDIATE</policy> </cancelPolicyCase> </cancelPolicy> <createAlignment> <createAlignmentCase> <alignment>START_OF_BUNDLE</alignment> </createAlignmentCase> </createAlignment> <billingAlignment> <billingAlignmentCase> <alignment>ACCOUNT</alignment> </billingAlignmentCase> </billingAlignment> <priceList> <priceListCase> <toPriceList>DEFAULT</toPriceList> </priceListCase> </priceList> </rules> <plans> <plan name="ashtanga-monthly"> <product>Ashtanga</product> <initialPhases> <phase type="TRIAL"> <duration> <unit>DAYS</unit> <number>30</number> </duration> <fixed> <fixedPrice> <!-- empty price implies $0 --> </fixedPrice> </fixed> </phase> </initialPhases> <finalPhase type="EVERGREEN"> <duration> <unit>UNLIMITED</unit> </duration> <recurring> <billingPeriod>MONTHLY</billingPeriod> <recurringPrice> <price> <currency>EUR</currency> <value>150.00</value> </price> <price> <currency>USD</currency> <value>175.00</value> </price> </recurringPrice> </recurring> </finalPhase> </plan> <plan name="flow-monthly"> <product>Flow</product> <initialPhases> <phase type="TRIAL"> <duration> <unit>DAYS</unit> <number>30</number> </duration> <fixed> <fixedPrice> <!-- empty price implies $0 --> </fixedPrice> </fixed> </phase> </initialPhases> <finalPhase type="EVERGREEN"> <duration> <unit>UNLIMITED</unit> </duration> <recurring> <billingPeriod>MONTHLY</billingPeriod> <recurringPrice> <price> <currency>EUR</currency> <value>100.00</value> </price> <price> <currency>USD</currency> <value>125.00</value> </price> </recurringPrice> </recurring> </finalPhase> </plan> <plan name="iyengar-monthly"> <product>Iyengar</product> <initialPhases> <phase type="TRIAL"> <duration> <unit>DAYS</unit> <number>30</number> </duration> <fixed> <fixedPrice> <!-- empty price implies $0 --> </fixedPrice> </fixed> </phase> </initialPhases> <finalPhase type="EVERGREEN"> <duration> <unit>UNLIMITED</unit> </duration> <recurring> <billingPeriod>MONTHLY</billingPeriod> <recurringPrice> <price> <currency>EUR</currency> <value>115.00</value> </price> <price> <currency>USD</currency> <value>150.00</value> </price> </recurringPrice> </recurring> </finalPhase> </plan> <plan name="pranayama-monthly"> <product>Pranayama</product> <initialPhases> <phase type="TRIAL"> <duration> <unit>DAYS</unit> <number>30</number> </duration> <fixed> <fixedPrice> <!-- empty price implies $0 --> </fixedPrice> </fixed> </phase> </initialPhases> <finalPhase type="EVERGREEN"> <duration> <unit>UNLIMITED</unit> </duration> <recurring> <billingPeriod>MONTHLY</billingPeriod> <recurringPrice> <price> <currency>EUR</currency> <value>25.00</value> </price> <price> <currency>USD</currency> <value>35.00</value> </price> </recurringPrice> </recurring> </finalPhase> </plan> </plans> <priceLists> <defaultPriceList name="DEFAULT"> <plans> <plan>ashtanga-monthly</plan> <plan>flow-monthly</plan> <plan>iyengar-monthly</plan> <plan>pranayama-monthly</plan> </plans> </defaultPriceList> </priceLists> </catalog>
You should now do the same kind of configuration for the other saas
tenant and you are ready to start creating account, subscriptions, invoices and make payments on both tenants in parallel!