How to Build SaaS Using Docker-based Tools — Part 2
This is the second part of my Docker orchestration tools review. As a sequel of How to Build SaaS Using Docker-based Tools — Part 1. Let’s try Kontena next.
Kontena CLI
The first impression of Kontena is “really cool” and “very developer friendly”. The first thing you need to do is to install Kontena CLI. I’ve installed it both on MacOS and Ubuntu by following the documentation and didn’t have any problems with that. After installation, you can start using it right away because it’s very simple to use and documentation is good enough (as opposed to Docker Cloud CLI).
For using Kontena, you need to have a master node — this is a machine that you could bootstrap on any cloud provider and that could be used as a central point for managing other nodes, grids, and services.
Supported cloud providers
At this moment, as mentioned in docs, Kontena supports 5 cloud providers: AWS, Azure, Digital Ocean, Packer and UpCloud. I’ve tried to setup it with Amazon Web Services. To start installing you master node on AWS, you need to setup AWS user with security credentials. Just go to AWS IAM and create a new user. For example, “kontena-master” or any other name. Make sure to check “Programmatic access” checkbox because you need to have access and secret keys. Then set permissions for the just created user by selecting “AmazonEC2FullAccess” policy.
On the last step, you’ll get “Access Key ID” and “Secret access key”. Save it somewhere in your project.
But that’s not all: you need to have an AWS EC2 Key Pair — these are keys you need for Kontena to be able to login into your nodes. Just switch your AWS Console to Amazon EC2 service and create a key pair. Once done, now you have all information to setup Kontena master node on AWS. Lets’ do this by using CLI command described in docs (don’t forget to change — storage
and — type
options according to your needs):
$ kontena aws master create \
--access-key <aws_master_key> \
--secret-key <aws_secret_key> \
--key-pair <aws_key_pair_name> \
--type m3.medium \
--storage 100 \
--region eu-west-1
During the process of master node creation, you will be asked for several questions. One is about authentication for accessing your Kontena managed infrastructure and recommended variant at this point is to go with authentication via Kontena Cloud. This is what I’ve chosen as well. But besides Kontena Cloud, any other OAuth2-compatible authentication providers could be used.
Grids, Stacks and Services
Let’s create the first grid: recommended initial node counts are 3,5,7 — according to etcd
rules. These nodes will form a etcd
cluster infrastructure — main nodes responsible for your cluster stability and failover.
$ kontena grid create --initial-size=<initial_node_count> aws-grid# Now let's create AWS EC2 node in the current grid$ kontena aws node create \
--access-key <aws_master_key> \
--secret-key <aws_secret_key> \
--key-pair <aws_key_pair_name> \
--type m4.medium \
--storage 100 \
--zone a \
--region eu-west-1
After your grid is ready for operating, you are becoming closer to prepare your infrastructure. Let’s say you have a web app that needs a database and of course, web server/load balancer. I’ve come to using micro-services approach and having each component in the separate container. There are two ways of organizing your containers:
- Via separate independent Services (that could be linked together)
- Via Stacks (which is the preferred way and recommended by Kontena team)
So Kontena Stack is actually an organized group of Services and Service is a group of containers using the same Docker image. The second option is what I’ve chosen and started preparing my stacks: stack for a web app, stack for DB, stack for a load balancer. You could use pre-defined Kontena images for database and load balancer, but you need to pack your web app into an image as well. I’ve already had one for my web app — APIQ CMS. So my services are Ruby on Rails app packed into Docker image, PostgreSQL and Load Balancer (web server with public HTTP exposure). Here are examples of my Stacks definition (you define it using YAML format and according to Kontena.yml)
# lb-stack.yml
stack: apiq/lb
description: APIQ LB
expose: internet_lb
services:
internet_lb:
image: kontena/lb:latest
ports:
- 80:80
- 443:443
deploy:
strategy: daemon
Need to note expose:
option here — this allows you to expose your service to other stacks.
# db-stack.yml
stack: apiq/db
description: APIQ DB
expose: db
services:
db:
image: postgres:latest
stateful: true
A database is a stateful service and I guess you want to store you databases data somewhere and database nodes removal won’t affect your data.
# kontena.yml
stack: apiq/core
description: APIQ Core
variables:
secret_token: # variable name
type: string # type (string, integer, boolean, uri, enum)
from: # where to obtain a value for this variable
random_string: 64 # still no value, auto generate a random
to:
env: SECRET_TOKEN # send this value to the vault on kontena master
services:
app:
image: webgradus/kms
command: bundle exec rails s -p 3000 -b '0.0.0.0'
environment:
SECRET_TOKEN: ${SECRET_TOKEN}
RAILS_SERVE_STATIC_FILES: 'true'
KONTENA_LB_INTERNAL_PORT: 3000
KMS_ASSETS_STORAGE: 'fog'
hooks:
post_start:
- name: db_create
cmd: rails db:create
instances: 1
oneshot: true
- name: db_migration
cmd: rails db:migrate
instances: 1
links:
- lb/internet_lb
- db/db
ports:
- "3000:3000"
secrets:
- secret: AWS_ACCESS_KEY_ID
name: AWS_ACCESS_KEY_ID
type: env
- secret: AWS_SECRET_ACCESS_KEY
name: AWS_SECRET_ACCESS_KEY
type: env
- secret: AWS_REGION
name: AWS_REGION
type: env
Let’s review this in details. First, variables
section. This section allows you to define variables and the way of obtaining them that could be used in Kontena.yml. In my Stack file, I’m generating a random string and pass it to SECRET_TOKEN
environment variable (that should be specified for the app). command
defines the way app should be launched, environment
— environment variables for an app as well as special Kontena variables (for example, KONTENA_LB_INTERNAL_PORT
that links web app and load balancer). Next, I’ve defined several hooks executing Rails-specific tasks necessary for correct app bootstrapping — database creation and migration. It’s pretty clear that links
section links app
service with db
and internet_lb
services exposed from first two stacks. And secrets
section is what Kontena Vault is responsible for — you’re specifying keys for API or database passwords accessible on the Grid level. In my example, I’m using Amazon S3 service in my web app and want any app
service node to have access to Amazon credentials. Prepare your secrets by saving them into Vault:
$ kontena vault write AWS_REGION us-east-1
Now we could install prepared stacks on nodes:
$ kontena stack install lb-stack.yml
$ kontena stack install db-stack.yml
$ kontena stack install --name cms-1 kontena.yml
After this, I’ve got APIQ CMS instance connected to database and load balancer and available on the web. To find out public IP and check out your app in browser:
$ kontena service show cms-1
Where is SaaS actually?
Ok, very good question :-) So now after you’ve tried everything using CLI, it’s time to perform the same operations via Kontena Master REST API. Just generate API token and start installing app stacks on your customer demand. Follow API documentation and write code in your favorite language.
$ kontena master token create --expires-in 0 --token
Kontena Cloud
Once you’ve signed up on Kontena Cloud and used it for authentication, you could login anytime and view the dashboard of your Kontena-based infrastructure (absolutely for free now). Really cool! There you’ll find your Grids, Nodes, Services, resources usage, and logs.
Community
Next thing about Kontena need to mention is very good support from the core team. I’ve asked two questions on Kontena forum and always got responses. Also, there are many new resources appearing: video guides on YouTube, the regular email digest of Kontena news and updates, as well as blog posts.
In conclusion, I’d recommend to give it a try even if you don’t need to build SaaS. You could just manage infrastructure for your app by easy scaling your services, adding more nodes and grids.