Background
Over the past couple of days, I’ve been playing with Tweed. Tweed is an on-demand service broker that targets both Kubernetes and BOSH for deploying mission-critical data services, in both shared configurations (running as containers on a Kubernetes cluster) and dedicated configurations (running as virtual machines in a cloud IaaS of your choice).
How to run Tweed
To run Tweed in Kubernetes you need a running cluster. I have my own cluster running on Linode. For testing purposes, the traffic to my cluster is terminated at *.nahmad.lke.starkandwayne.com. Tweed hosts an evaluation deployment on GitHub https://github.com/tweedproject/tweed/blob/master/eval.yml
This basically sets up everything you need to play with Tweed. By default, it sets up everything under the Tweed namespace. Once the pods are running you can go ahead and start playing with the CLI
$ kubectl get podsNAME READY STATUS RESTARTS AGE
broker-6dfd54ccb7-q9m6s 2/2 Running 0 44h
Setting up Tweed CLI
There are basically two ways of running the CLI locally.
- Using the Tweed CLI via kubectl
$ alias tweed='kubectl exec -it -n tweed -c broker $(kubectl -n tweed get po -l app=tweed -o name) -- tweed'
- The more fun way is to build the CLI yourself. To do that, go to
https://github.com/tweedproject/tweed/tree/master/cmd/tweed and do a go build
$ ls
args.go binding.go broker.go check.go deprovision.go files.go instances.go log.go oops.go patience.go purge.go task.go utils.go
bind.go bindings.go catalog.go client.go file.go instance.go json.go main.go opts.go provision.go reset.go unbind.go wait.go
$ tweed git:(master) go build
$ ls
args.go binding.go broker.go check.go deprovision.go files.go instances.go log.go oops.go patience.go purge.go task.go unbind.go wait.go
bind.go bindings.go catalog.go client.go file.go instance.go json.go main.go opts.go provision.go reset.go tweed utils.go
Hurray! We built the Tweed CLI that you can use. The easiest way is to configure an alias for the binary you just built OR you can be a UNIX/Linux maestro and hook the alias up into the shell.
$ alias tweed=<path_to_the_binary>
Environment variables needed for Tweed
- $TWEED_URL
- $TWEED_USERNAME
- $TWEED_PASSWORD
There’s an eval.envrc
at the root of the Tweed project that you can use so that you don’t have to. It basically extracts the Tweed related information from the Kubernetes cluster and saves that as corresponding env variables. You need direnv
installed on your system to be able to do this. On macOS, you can use homebrew to install it via brew install direnv
. Before each prompt, direnv
checks for the existence of a .envrc
file in the current and parent directories. If the file exists (and is authorized), it is loaded into a BASH sub-shell and all exported variables are then captured by direnv and then made available to the current shell. You can rename the eval.envrc
to .envrc
& then do a direnv allow
, so that whenever you navigate to the Tweed repo, your env variables are set up automatically.
Catalog
Once we are all set with the CLI and the env variables, we can go ahead and check what kind of services and plans are available via Tweed using tweed catalog
. Catalog exposes the services and instances.
$ tweed catalog
Service / Plan # Free?
============== = =====
postgres/v9 0/2 no A standalone, single-node PostgreSQL RDBMS
PostgreSQL version 9.x
[tags: postgres, psql, pg, shared]
postgres/v10 0/1 no A standalone, single-node PostgreSQL RDBMS
PostgreSQL version 10.x
[tags: postgres, psql, pg, shared]
postgres/v11 0/1 no A standalone, single-node PostgreSQL RDBMS
PostgreSQL version 11.x
[tags: postgres, psql, pg, shared]
This basically lists the name of the service and plans. Services and plans come from the stencil definitions. Stencils are basically the patterns/recipes for different services. You can find all the stencils at https://github.com/tweedproject/tweed/tree/master/stencils. The configuration of stencils is injected while deploying Tweed. In our case, eval.yml
took care of this. There’s a quota assigned to it which basically lists (num of instances running)/(max/total allowed).
Tweed’s Lifecycle
Tweed follows Open Service Broker (OSB)‘s lifecycle approach for deploying dedicated/shared services. A typical lifecycle looks as follows:
Provision → Bind → Unbind → Deprovision
Provisioning a service:
You can provision a service using the following command:
Format: $ tweed provision service plan
$ tweed provision postgres v11
service instance scheduled for provisioning; thank you for your patience.
instance i-c78ec9754454bf is still provisioning; sleeping for another 2 seconds...
instance i-c78ec9754454bf is still provisioning; sleeping for another 2 seconds...
instance i-c78ec9754454bf is still provisioning; sleeping for another 2 seconds...
instance i-c78ec9754454bf is still provisioning; sleeping for another 2 seconds...
instance i-c78ec9754454bf is still provisioning; sleeping for another 2 seconds...
instance i-c78ec9754454bf is still provisioning; sleeping for another 2 seconds...
instance i-c78ec9754454bf is still provisioning; sleeping for another 2 seconds...
instance i-c78ec9754454bf is still provisioning; sleeping for another 2 seconds...
instance i-c78ec9754454bf is still provisioning; sleeping for another 2 seconds...
instance i-c78ec9754454bf is still provisioning; sleeping for another 2 seconds...
instance i-c78ec9754454bf is still provisioning; sleeping for another 2 seconds...
instance i-c78ec9754454bf is still provisioning; sleeping for another 2 seconds...
instance i-c78ec9754454bf is still provisioning; sleeping for another 2 seconds...
instance i-c78ec9754454bf is still provisioning; sleeping for another 2 seconds...
instance i-c78ec9754454bf is still provisioning; sleeping for another 2 seconds...
instance i-c78ec9754454bf is still provisioning; sleeping for another 2 seconds...
instance i-c78ec9754454bf is still provisioning; sleeping for another 2 seconds...
instance i-c78ec9754454bf is still provisioning; sleeping for another 2 seconds...
instance i-c78ec9754454bf is still provisioning; sleeping for another 2 seconds...
instance i-c78ec9754454bf is still provisioning; sleeping for another 2 seconds...
instance i-c78ec9754454bf is no longer provisioning.
instance: i-c78ec9754454bf
run tweed instance i-c78ec9754454bf for more details.run tweed bind i-c78ec9754454bf to get some credentials.
The output is pretty neat and detailed. You can run tweed instance id
to get more details out of the instance.
$ tweed instance i-c78ec9754454bf
id: i-c78ec9754454bf
state: quiet
service: postgres
plan: v11
params:
bindings: (none)
When we provisioned our instance, Tweed went ahead and set up a namespace for our instance (The name of this namespace is the same as our instance). Let’s check this namespace and see what Tweed did under the hood.
$ kubens i-c78ec9754454bf
Context "lke11803-ctx" modified.
Active namespace is "i-c78ec9754454bf".
$ kubectl get all
NAME READY STATUS RESTARTS AGE
pod/postgres-65f59d8c9f-chgj6 1/1 Running 0 16m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/postgres NodePort 10.128.53.224 <none> 5432:31051/TCP 16m
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/postgres 1/1 1 1 16m
NAME DESIRED CURRENT READY AGE
replicaset.apps/postgres-65f59d8c9f 1 1 1 16m
List the provisioned services using tweed ls
$ tweed ls
ID State Service Plan
== ===== ======= ====
i-c78ec9754454bf quiet postgres v11
Bind a service:
So far we have a running Postgres v11 instance. We don’t have the auth creds or other information necessary e.g. ports etc. For this, we need to bind our instance.
Format: $ tweed bind instance
$ tweed bind i-c78ec9754454bf
service instance bind operation scheduled; thank you for your patience.
instance i-c78ec9754454bf is still binding; sleeping for another 2 seconds...
instance i-c78ec9754454bf is still binding; sleeping for another 2 seconds...
instance i-c78ec9754454bf is no longer binding.
binding: b-da44ceccef0799
run tweed instance i-c78ec9754454bf for more details.
run tweed bindings i-c78ec9754454bf to show all bound credentials.
We can run tweed instance
again to look at the credentials
$ tweed instance i-c78ec9754454bf
id: i-c78ec9754454bf
state: quiet
service: postgres
plan: v11
params:
bindings:
b-da44ceccef0799:
{
"database": "pg1",
"host": "45.79.19.178",
"hosts": [
"45.79.19.178",
"45.79.38.253"
],
"password": "s/8cf2fde1-d1da-41e2-9244-086bdeedaf38",
"port": 31051,
"tryit": "PGPASSWORD=s/8cf2fde1-d1da-41e2-9244-086bdeedaf38 psql -h 45.79.19.178 -p 31051 -U ueeebc0ff pg1",
"username": "ueeebc0ff",
"version": 11
}
What Tweed here did was to create a randomized user account, provisioned a randomized password for it, and granted it access to the system. The tryit credential that Tweed gave us can be used to log into the Postgres instance.
$ PGPASSWORD=s/8cf2fde1-d1da-41e2-9244-086bdeedaf38 psql -h 45.79.19.178 -p 31051 -U ueeebc0ff pg1
psql (13.0, server 11.9 (Debian 11.9-1.pgdg90+1))
Type "help" for help.
pg1=#
Woot woot! We’re in the instance now.
Tweed logs
Another neat feature of Tweed is its ability to provide detailed logs. You can extract them by using tweed logs service-instance
$ tweed logs i-801ed0e83fc813
Wed Oct 21 18:32:34 UTC 2020 :: tweed
-----------------------------------------------------------------------------------
######## ######## ####### ## ## #### ###### #### ####### ## ##
## ## ## ## ## ## ## ## ## ## ## ## ## ## ### ##
## ## ## ## ## ## ## ## ## ## ## ## ## #### ##
######## ######## ## ## ## ## ## ###### ## ## ## ## ## ##
## ## ## ## ## ## ## ## ## ## ## ## ## ####
## ## ## ## ## ## ## ## ## ## ## ## ## ## ###
## ## ## ####### ### #### ###### #### ####### ## ##
-----------------------------------------------------------------------------------
infrastructure: /tweed/etc/infrastructures/k8s (kubernetes)
stencil: /tweed/etc/stencils/postgres/standalone
/tweed/data/instances/i-801ed0e83fc813>
GENERATING CREDENTIALS
======================
>> generating a new, random, postgres root username
username: ua85c5550
>> generating a new, random, postgres root username
password: s/5af52494-6c4b-452f-b9ab-b196e00ec480
>> setting the instance database name to 'pg1'
name: pg1
root account is 'ua85c5550'
RENDERING RESOURCE TEMPLATES
============================
>> rendering k8s/ns.yml ...
>> rendering k8s/svc.yml ...
>> rendering k8s/dep.yml ...
>> rendering k8s/pvc.yml ...
kubernetes spec generated!
>> applying out/spec.yml via `kubectl'...
namespace/i-801ed0e83fc813 created
service/postgres created
deployment.apps/postgres created
persistentvolumeclaim/postgres created
>> waiting for pods to become 'ready'...
deployment.apps/postgres condition met
done.
NAME READY STATUS RESTARTS AGE
pod/postgres-5674ddcf8f-kkgww 1/1 Running 0 28s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/postgres NodePort 10.128.87.67 <none> 5432:30279/TCP 28s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/postgres 1/1 1 1 29s
NAME DESIRED CURRENT READY AGE
replicaset.apps/postgres-5674ddcf8f 1 1 1 29s
Wed Oct 21 18:33:18 UTC 2020 :: tweed
---------------------------------------
######## #### ## ## ########
## ## ## ### ## ## ##
## ## ## #### ## ## ##
######## ## ## ## ## ## ##
## ## ## ## #### ## ##
## ## ## ## ### ## ##
######## #### ## ## ########
[b-c4e9ac2bdf7d0b]
---------------------------------------
infrastructure: /tweed/etc/infrastructures/k8s (kubernetes)
stencil: /tweed/etc/stencils/postgres/standalone
/tweed/data/instances/i-801ed0e83fc813>
GENERATING CREDENTIALS
======================
>> bound user is 'u8259a306'
username: u8259a306
password: s/737618fe-3071-4e32-a11d-7faabe27370d
PROCESSING GRANTS AGAINST POSTGRES
==================================
>> attempting to use the following postgres endpoints:
- 45.79.19.178:30279
- 45.79.38.253:30279
trying nodes [45.79.19.178
45.79.38.253]
CREATE DATABASE
CREATE ROLE
CREATE DATABASE
>> created new user u8259a306, via 45.79.19.178:30279!
b-c4e9ac2bdf7d0b: <$stdin
binding saved to vault at [secret/boss/i-801ed0e83fc813/tweed/bindings:b-c4e9ac2bdf7d0b]
BIND COMPLETE.
EXITING 0
Let’s proceed to the other half of the lifecycle.
Unbind a service
Format: $ tweed unbind instance/binding
$ tweed unbind i-c78ec9754454bf/b-da44ceccef0799
service instance unbind operation scheduled; thank you for your patience.
instance i-c78ec9754454bf is still unbinding; sleeping for another 2 seconds...
instance i-c78ec9754454bf is no longer unbinding.
run tweed instance i-c78ec9754454bf for more details.
Deprovision a service instance:
You can use tweed deprovision instance
to tear it down.
$ tweed deprovision i-c78ec9754454bf
service instance scheduled for deprovisioning; thank you for your patience.
instance i-c78ec9754454bf is not yet gone; sleeping for another 2 seconds...
instance i-c78ec9754454bf is not yet gone; sleeping for another 2 seconds...
instance i-c78ec9754454bf is not yet gone; sleeping for another 2 seconds...
instance i-c78ec9754454bf is not yet gone; sleeping for another 2 seconds...
instance i-c78ec9754454bf is not yet gone; sleeping for another 2 seconds...
instance i-c78ec9754454bf is not yet gone; sleeping for another 2 seconds...
instance i-c78ec9754454bf is not yet gone; sleeping for another 2 seconds...
instance i-c78ec9754454bf is now gone.
run tweed instance i-c78ec9754454bf for more historical information.
run tweed purge i-c78ec9754454bf to remove all trace of this service instance.
If we want to remove all traces of this instance, we can simply purge it
$ tweed purge i-c78ec9754454bf
service instance 'i-c78ec9754454bf' purged
Some hidden gems:
JSON and jq for scripting
We can use JSON and jq
to extract information from Tweed. This can be useful for automation and scripting. e.g. to extract the ID of the Postgres instance we ran, we can use:
$ tweed ls --json | jq -r ' .[] | select(.service == "postgres") | .id'
i-c78ec9754454bf
tweed check
This command lists the viability of a plan.
Format: $ tweed check service plan
$ tweed check postgres v11
service 'PostgreSQL' / plan 'v11' is viable