Quickstart for Cloud Databases#

This guide introduces you to Rackspace Cloud Databases. Cloud Databases is a MySQL relational database service that allows you to easily provision instances without having all the maintenance overhead. You can also back up your databases on a regular basis and set up monitoring functionality to track usage.

Concepts#

To use this service effectively, you should understand how these key ideas are used in this context:

flavor

A flavor is an available hardware configuration for a database instance. Each flavor has a unique combination of memory capacity and priority for CPU time.

instance

An isolated environment in which your databases run. This is similar to the virtualized instances used by Cloud Servers, but database instances are optimized for database performance.

Authentication#

To use this service you have to authenticate first. To do this, you will need your Rackspace username and API key. Your username is the one you use to login to the Cloud Control Panel at http://mycloud.rackspace.com/.

To find your API key, use the instructions in View and reset your API key.

You can specify a default region. Here is a list of available regions:

  • DFW (Dallas-Fort Worth, TX, US)

  • HKG (Hong Kong, China)

  • IAD (Blacksburg, VA, US)

  • LON (London, England)

  • SYD (Sydney, Australia)

Some users have access to another region in ORD (Chicago, IL). New users will not have this region.

Once you have these pieces of information, you can pass them into the SDK by replacing {username}, {apiKey}, and {region} with your info:

CloudIdentity cloudIdentity = new CloudIdentity()
{
    APIKey = "{apiKey}",
    Username = "{username}"
};
CloudIdentityProvider cloudIdentityProvider = new CloudIdentityProvider(cloudIdentity);
UserAccess userAccess = cloudIdentityProvider.Authenticate(cloudIdentity);
CloudDatabasesProvider cloudDatabasesProvider = new CloudDatabasesProvider(
  cloudIdentity,
  "{region}",
  null
);
import (
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/rackspace"
osDatabases "github.com/rackspace/gophercloud/openstack/db/v1/databases"
osUsers "github.com/rackspace/gophercloud/openstack/db/v1/users"
"github.com/rackspace/gophercloud/rackspace/db/v1/backups"
"github.com/rackspace/gophercloud/rackspace/db/v1/databases"
"github.com/rackspace/gophercloud/rackspace/db/v1/flavors"
"github.com/rackspace/gophercloud/rackspace/db/v1/instances"
"github.com/rackspace/gophercloud/rackspace/db/v1/users"
)

ao := gophercloud.AuthOptions{
Username: "{username}",
APIKey: "{apiKey}",
}
provider, err := rackspace.AuthenticatedClient(ao)

serviceClient, err := rackspace.NewDBV1(provider, gophercloud.EndpointOpts{
Region: "{region}",
})
// Authentication in jclouds is lazy and happens on the first call to the cloud.
TroveApi troveApi = ContextBuilder.newBuilder("rackspace-clouddatabases-us")
    .credentials("{username}", "{apiKey}")
    .buildApi(TroveApi.class);
pkgcloud = require('pkgcloud');

var rackspace = pkgcloud.database.createClient({
  provider: 'rackspace',
  username: '{username}',
  apiKey: '{apiKey}',
  region: '{region}'
});
require 'vendor/autoload.php';

use OpenCloud\Rackspace;

$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array(
    'username' => '{username}',
    'apiKey'   => '{apiKey}'
));
import pyrax

pyrax.set_credentials('{username}', '{apiKey}', region='{region}')
cdb = pyrax.cloud_databases
require 'fog'

@client = Fog::Rackspace::Databases.new(
  :rackspace_username => '{username}',
  :rackspace_api_key => '{apiKey}',
  :rackspace_region => '{region}'
)
# {username}, {apiKey} below are placeholders, do not enclose '{}' when you replace them with actual credentials.

curl -s -X POST https://identity.api.rackspacecloud.com/v2.0/tokens \
  -H "Content-Type: application/json" \
  -d '{
    "auth": {
      "RAX-KSKEY:apiKeyCredentials": {
        "username": "{username}",
        "apiKey": "{apiKey}"
      }
    }
  }' | python -m json.tool

# From the resulting json, set two environment variables: TOKEN and ENDPOINT.

export TOKEN="{tokenId}"
export ENDPOINT="{publicUrl}" # For the Cloud Databases service

Use the API#

Some of the basic operations you can perform with this API are described below.

Work with instances#

Two common issues that developers have with a traditional RDS relate to peformance: increasing the amount of IO transactions per second, for example, or reducing application latency. Because of this, our instances were designed from the ground up with two primary features in mind:

  • Performance. By using container-based virtualization, instances are allocated the resources they truly need and there is no compute power waste such as with traditional virtualization. As a result, things are faster and more efficient.

  • Reliability. We use fault-tolerant components, such as: RAID levels for individual drives, multi-tenant environments across different nodes, as well as dedicated power supplies and network adapters. This means you have redundancy both from a hardware level and a software level.

Create an instance#

To create an instance, you must decide on the hardware type, or flavor, that you want to use:

FlavorId flavorId = new FlavorId("{flavorId}");
DatabaseFlavor databaseFlavor = await cloudDatabasesProvider.GetFlavorAsync(
  flavorId, CancellationToken.None);
flavor, err := flavors.Get(serviceClient, "{flavorId}").Extract()
FlavorApi flavorApi = troveApi.getFlavorApi("{region}");

Flavor flavor = flavorApi.get("{flavorId}");
client.getFlavor({flavorId}, function(err, flavor) {
  if (err) {
    // TODO handle as appropriate
  }

  // TODO use your flavor here
});
$flavor = $service->flavor('{flavorId}');
flavor = cdb.get_flavor('{flavorId}')
flavor = @client.flavors.get('{flavorId}')
curl -s $ENDPOINT/flavors/{flavorId} \
  -H "X-Auth-Token: $TOKEN" \
  -H "Accept: application/json" | python -m json.tool

Alternatively, you can traverse through the standard list Rackspace provides:

ReadOnlyCollection<DatabaseFlavor> databaseFlavors = await cloudDatabaseProvider
  .ListFlavorsAsync(CancellationToken.None);
err := flavors.List(serviceClient).EachPage(func(page pagination.Page) (bool, error) {
  flavorList, err := flavors.ExtractFlavors(page)
  if err != nil {
    return false, err
  }
  for _, f := range flavorList {
    // ...
  }
  return true, nil
})
// List your flavors and get the first.
FlavorApi flavorApi = troveApi.getFlavorApi("{region}");

FluentIterable<Flavor> flavors = flavorApi.list();
client.getFlavors(function(err, flavors) {
  if (err) {
    // TODO handle as appropriate
  }

  // TODO use your flavors array here
});
$flavors = $service->flavorList();
flavors = cdb.list_flavors()
flavors = @client.flavors.all
curl -s $ENDPOINT/flavors \
  -H "X-Auth-Token: $TOKEN" \
  -H "Accept: application/json" | python -m json.tool

# Choose the flavor you prefer and make a note of its "href" element.
export FLAVOR_REF="{flavorRef}"

# Remember the same "href" of another flavor for resizing, later.
export NEW_FLAVOR_REF="{newFlavorRef}"

Once you have this flavor, you can use it to create your instance:

FlavorRef flavorRef = new FlavorRef(databaseFlavor.Id);
DatabaseVolumeConfiguration databaseVolumeConfiguration = new DatabaseVolumeConfiguration(
  {database_volume_configuration_id}
);
DatabaseInstanceConfiguration databaseInstanceConfiguration = new DatabaseInstanceConfiguration(
  flavorRef,
  databaseVolumeConfiguration,
  "sample_instance"
);

DatabaseInstance databaseInstance = await cloudDatabasesProvider.CreateDatabaseInstanceAsync(
  databaseInstanceConfiguration,
  AsyncCompletionOption.RequestCompleted,
  CancellationToken.None,
  null
);
createOpts := instances.CreateOpts{
  FlavorRef: "{flavorRef}",
  Size: 20,
  Name: "sample_instance",
}
databaseInstance, err := instances.Create(serviceClient, createOpts).Extract()
TroveUtils utils = new TroveUtils(troveApi);

Instance instance = utils.getWorkingInstance("{region}", "{instanceName}", "{flavorId}", 1);
// For this example, we'll create a 20GB instance using the flavor with the
// least RAM.

flavors = flavors.sort(function(a, b) {
  return a.ram >= b.ram;
});

client.createInstance({
  flavor: flavors[0],
  name: 'sample_instance',
  size: 20
}, function(err, instance) {
  if (err) {
    // TODO handle as appropriate
  }

  // TODO use your newly created instance here
});
$dbService = $client->databaseService(null, '{region}');

// Create the empty object:
$instance = $dbService->instance();

// Create your volume object, which in this case sets the size as 20GB:
$volume = (object) array('size' => 20);

// Execute the create API operation
$instance->create(array(
    'name'   => 'sample_instance',
    'volume' => $volume,
    'flavor' => $flavor
));
# For this example, we'll create a 20GB instance using the flavor with the
# least RAM.

flavors = cdb.list_flavors()
flavors.sort(key=lambda flav: flav.ram)
flavor = flavors[0]
inst = cdb.create("sample_instance", volume=20, flavor=flavor)
# For this example, we'll create a 20GB instance using the flavor with the
# least RAM.

flavor = @client.flavors.sort_by(&:ram)[0]
instance = @client.instances.create(
  :name => 'sample_instance',
  :volume_size => 20,
  :flavor_id => flavor.id
)
instance.wait_for { ready? }
curl -s -X POST $ENDPOINT/instances \
  -H "X-Auth-Token: $TOKEN" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{
    \"instance\": {
      \"name\": \"sample_instance\",
      \"flavorRef\": \"$FLAVOR_REF\",
      \"volume\": { \"size\": 20 }
    }
  }" | python -m json.tool

Resize an instance#

As with creating an instance, in order to resize one, you need to know which flavor to use. Once you’ve decided on a new flavor, you can use it to resize your running instance:

await cloudDatabasesProvider.ResizeDatabaseInstanceAsync(databaseInstance.Id,
  new FlavorRef("{newFlavorId}"),
  AsyncCompletionOption.RequestCompleted,
  CancellationToken.None,
  null
);
err := instances.Resize(serviceClient, "{instanceId}", "{newFlavorId}").ExtractErr()
// Not currently supported by this SDK
client.setFlavor(instance, {newFlavor}, function(err) {
  if (err) {
    // TODO handle err as appropriate
  }
});
$instance->resize($flavor);
inst.resize(new_flavor)
instance.resize(new_flavor.id)
instance.wait_for { ready? }
curl -s -X POST $ENDPOINT/instances/{instanceId}/action \
  -H "X-Auth-Token: $TOKEN" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{
    \"resize\": { \"flavorRef\": \"$NEW_FLAVOR_REF\" }
  }" | python -m json.tool

Restart an instance#

In order to restart your instance:

await cloudDatabasesProvider.RestartDatabaseInstanceAsync(
  databaseInstance.Id,
  AsyncCompletionOption.RequestCompleted,
  CancellationToken.None,
  null
);
err := instances.Restart(serviceClient, "{instanceId}").ExtractErr()
// Not currently supported by this SDK
client.restartInstance(instance, function(err) {
  if (err) {
    // TODO handle err as appropriate
  }
});
$instance->restart();
inst.restart()
instance.restart
instance.wait_for { ready? }
curl -s -X POST $ENDPOINT/instances/{instanceId}/action \
  -H "X-Auth-Token: $TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{ "restart": {} }' | python -m json.tool

Enable root user#

Although you create a default user when creating a database instance, sometimes it might be necessary to execute operations as the root user. To do so, you will need to enable root:

RootUser rootUser = await cloudDatabasesProvider.EnableRootUserAsync(
  databaseInstance.Id,
  CancellationToken.None
);
user, err := instances.EnableRootUser(serviceClient, "{instanceId}").Extract()
InstanceApi instanceApi = troveApi.getInstanceApi("{region}");

String password = instanceApi.enableRoot("{instanceId}");
client.enableRoot(instance, function(err) {
  if (err) {
    // TODO handle err as appropriate
  }
});
/** @param $user OpenCloud\Database\Resource\User */
$user = $instance->enableRootUser();

// Store the root password to your local filesystem.
file_put_contents('~/.root_mysql_pwd', $user->password);
inst.enable_root_user()
instance.enable_root_user

# Store the root password to your local filesystem.
File.write('~/.root_mysql_pwd', instance.root_password)
curl -s -X POST $ENDPOINT/instances/{instanceId}/root \
  -H "X-Auth-Token: $TOKEN" \
  -H "Accept: application/json" | python -m json.tool

This operation returns the root password for your use. Note that changes you make as a root user may cause detrimental effects to the database instance and unpredictable behavior for API operations.

If you’re not sure whether you’ve already enabled the root user, you can easily query whether root is enabled or not:

bool? isRooted = await cloudDatabasesProvider.CheckRootEnabledStatusAsync(
  databaseInstance.Id,
  CancellationToken.None
);
isRootEnabled, err := instances.IsRootEnabled(serviceClient, "{instanceId}")
InstanceApi instanceApi = troveApi.getInstanceApi("{region}");

instanceApi.isRooted("{instanceId}");
client.rootEnabled(instance, function(err, rootEnabled) {
  if (err) {
    // TODO handle err as appropriate
  }

  // TODO check status of rootEnabled
});
$instance->isRootEnabled();
root_enabled = cdb.root_user_status()
root_enabled = instance.root_user_enabled?
curl -s -X GET $ENDPOINT/instances/{instanceId}/root \
  -H "X-Auth-Token: $TOKEN" \
  -H "Accept: application/json" | python -m json.tool

Create database#

This is a simple MySQL database that you interact with normally. Creating one is very easy:

DatabaseName databaseName = new DatabaseName("sample_db");
DatabaseConfiguration databaseConfiguration = new DatabaseConfiguration(databaseName);

await cloudDatabasesProvider.CreateDatabaseAsync(
  databaseInstance.Id,
  databaseConfiguration,
  CancellationToken.None
);
createOpts := osDatabases.BatchCreateOpts{
  {
    Name: "sample_db",
  },
}
err := databases.Create(serviceClient, "{instanceId}", createOpts).ExtractErr()
DatabaseApi databaseApi = troveApi.getDatabaseApi("{region}", "{instanceId}");

databaseApi.create("{databaseName}");
client.createDatabase({
  name: 'sample_db',
  instance: instance
}, function(err, db) {
  if (err) {
    // TODO handle err as appropriate
  }

  // TODO use your newly created DB here
});
$db = $instance->database();

$db->create(array(
    'name' => 'sample_db'
));
inst.create_database('sample_db')
database = instance.databases.create(:name => 'sample_db')
curl -s -X POST $ENDPOINT/instances/{instanceId}/databases \
  -H "X-Auth-Token: $TOKEN" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "databases": [
      { "name": "sample_db" }
    ]
  }' | python -m json.tool

Create user#

To allocate a new user to a database:

DatabaseInstanceId databaseInstanceId = new DatabaseInstanceId("{database_instance_id}");
DatabaseName databaseName = new DatabaseName("{databaseName}");
UserName userName = new UserName("{username}");
UserConfiguration userConfiguration =
     new UserConfiguration(userName, "{password}", new DatabaseName[] { databaseName });
await cloudDatabasesProvider.CreateUserAsync(
     databaseInstanceId,
     userConfiguration,
     CancellationToken.None);
createOpts := osUsers.BatchCreateOpts{
  {
    Name: "{dbUsername}",
    Password: "{dbPassword}",
    Databases: osDatabases.BatchCreateOpts{
      {Name: "{dbName1}"},
      {Name: "{dbName2}"},
    },
  },
}
err := users.Create(serviceClient, "{instanceId}", createOpts).ExtractErr()
// Create a user with username and password and give them access to one database.
UserApi userApi = troveApi.getUserApi("{region}", "{instanceId}");

userApi.create("{dbUsername}", "{dbPassword}", "{dbName}");
client.createUser({
  instance: instance,
  username: {dbUsername},
  password: {dbPassword},
  databases: [ {dbName1}, {dbName2} ]
}, function(err, user) {
  if (err) {
    // TODO handle err as appropriate
  }

  // TODO use your newly created user here
});
$user = $instance->user();

// Create a user by specifying a username and password, and give them access
// to two databases.
$user->create(array(
    'name'      => '{dbUsername}',
    'password'  => '{dbPassword}',
    'databases' => array('{dbName1}', '{dbName2}')
));
# Create a user by specifying the username and password, and give them
# access to two databases.
inst.create_user("{dbUsername}", "{dbPassword}", ["{dbName1}", "{dbName2}"])
# Create a user by specifying the username and password, and give them
# access to two databases.

instance.users.create(
  :name => '{dbUsername}',
  :password => '{dbPassword}',
  :databases => ['{dbName1}', '{dbName2}']
)
curl -s -X POST $ENDPOINT/instances/{instanceId}/users \
  -H "X-Auth-Token: $TOKEN" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "users": [
      {
        "databases": [
          { "name": "{dbName1}" },
          { "name": "{dbName2}" }
        ],
        "name": "{dbUsername}",
        "password": "{dbPassword}"
      }
    ]
  }' | python -m json.tool

The user is granted all privileges on this database. Please bear in mind that root is a reserved name and cannot be used.

Backups#

Create backup#

To create a backup for your instance:

BackupConfiguration backupConfiguration = new BackupConfiguration(
  databaseInstance.Id,
  "backup_name",
  "friendly description"
);

Backup backup = await cloudDatabasesProvider.CreateBackupAsync(
  backupConfiguration,
  AsyncCompletionOption.RequestCompleted,
  CancellationToken.None,
  null
);
createOpts := backups.CreateOpts{
  Name: "backup_name",
  InstanceID: "{instanceId}",
}
backup, err := backups.Create(serviceClient, createOpts).Extract()
// Not currently supported by this SDK
// Not currently supported by this SDK
// Not currently supported by this SDK
backup = inst.create_backup("backup_name")
# Not currently supported by this SDK
curl -s -X POST $ENDPOINT/backups \
  -H "X-Auth-Token: $TOKEN" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "backup": {
      "instance": "{instanceId}",
      "name": "backup_name",
      "description": "friendly description"
    }
  }' | python -m json.tool

# Note the ID of the created backup for later.
export BACKUP_ID="{backupId}"

When creating a backup, there are some things to bear in mind:

  • During the backup process, MyISAM writes will be disabled. InnoDB is completely unaffected.

  • You also cannot add or delete databases or users during this process.

Restore instance from backup#

To restore your instance from a backup:

// To restore a database, you must first use an existing backup to
// create a Restore Point, then use that Restore Point to create
// a database instance.
RestorePoint restorePoint = new RestorePoint(backup.Id);
FlavorRef flavorRef = new FlavorRef(databaseFlavor.Id);

// Create a 20 GB volume.
DatabaseVolumeConfiguration databaseVolumeConfiguration = new DatabaseVolumeConfiguration(20);
DatabaseInstanceConfiguration databaseInstanceConfiguration = new DatabaseInstanceConfiguration(
  flavorRef,
  databaseVolumeConfiguration,
  "Restored",
  restorePoint
);

DatabaseInstance databaseInstance = await cloudDatabasesProvider.CreateDatabaseInstanceAsync(
  databaseInstanceConfiguration,
  AsyncCompletionOption.RequestCompleted,
  CancellationToken.None,
  null
);

// This operation is currently not supported through the .NET SDK.
createOpts := instances.CreateOpts{
  FlavorRef: "{flavorRef}",
  Size: 20,
  Name: "Restored",
  RestorePoint: "{backupId}",
}
databaseInstance, err := instances.Create(serviceClient, createOpts).Extract()
// Not currently supported by this SDK
// Not currently supported by this SDK
// Not currently supported by this SDK
# When restoring from a backup, you must supply a backup (either the backup
# object or its ID), a name for the new instance to be created from the
# backup, as well as a flavor and size (in GB) for the instance.
new_inst = cdb.restore_backup(backup, "Restored", flavor=flavor,
                              volume=20)
# Not currently supported by this SDK
curl -s -X POST $ENDPOINT/instances \
  -H "X-Auth-Token: $TOKEN" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d "{
    \"instance\": {
      \"name\": \"Restored\",
      \"flavorRef\": \"$FLAVOR_REF\",
      \"volume\": { \"size\": 20 },
      \"restorePoint\": {
        \"backupRef\": \"$BACKUP_ID\"
      }
    }
  }" | python -m json.tool

More information#

This quickstart is intentionally brief, demonstrating only a few basic operations. To learn more about interacting with Rackspace cloud services, explore the following sites: