A beginner’s introduction to Tweed

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.

  1. 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'

  1. 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

  1. $TWEED_URL
  2. $TWEED_USERNAME
  3. $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

Spread the word

twitter icon facebook icon linkedin icon