Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backup tag #10

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 54 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ Download the latest release from the [Releases page](https://github.com/maorfr/c

### From source

```
```shell
mkdir -p $GOPATH/src/github.com/maorfr && cd $_
git clone https://github.com/maorfr/cain.git && cd cain
make
Expand All @@ -41,14 +41,15 @@ make
### Backup Cassandra cluster to cloud storage

Cain performs a backup in the following way:

1. Backup the `keyspace` schema (using `cqlsh`).
1. Get backup data using `nodetool snapshot` - it creates a snapshot of the `keyspace` in all Cassandra pods in the given `namespace` (according to `selector`).
2. Copy the files in `parallel` to cloud storage using [Skbn](https://github.com/maorfr/skbn) - it copies the files to the specified `dst`, under `namespace/<cassandrClusterName>/keyspace/<keyspaceSchemaHash>/tag/`.
3. Clear all snapshots.
2. Get backup data using `nodetool snapshot` - it creates a snapshot of the `keyspace` in all Cassandra pods in the given `namespace` (according to `selector`).
3. Copy the files in `parallel` to cloud storage using [Skbn](https://github.com/maorfr/skbn) - it copies the files to the specified `dst`, under `namespace/<cassandrClusterName>/keyspace/<keyspaceSchemaHash>/tag/`.
4. Clear all snapshots.

#### Usage

```
```shell
$ cain backup --help
backup cassandra cluster to cloud storage

Expand All @@ -60,27 +61,43 @@ Flags:
--cassandra-data-dir string cassandra data directory. Overrides $CAIN_CASSANDRA_DATA_DIR (default "/var/lib/cassandra/data")
-c, --container string container name to act on. Overrides $CAIN_CONTAINER (default "cassandra")
--dst string destination to backup to. Example: s3://bucket/cassandra. Overrides $CAIN_DST
-h, --help help for backup
-k, --keyspace string keyspace to act on. Overrides $CAIN_KEYSPACE
-n, --namespace string namespace to find cassandra cluster. Overrides $CAIN_NAMESPACE (default "default")
-p, --parallel int number of files to copy in parallel. set this flag to 0 for full parallelism. Overrides $CAIN_PARALLEL (default 1)
-w, --password string password for the cassandra connection. Overrides $CAIN_PASSWORD (default "cassandra")
-l, --selector string selector to filter on. Overrides $CAIN_SELECTOR (default "app=cassandra")
-t, --tag string tag to backup, if empty then will use current timestamp. Use with caution - if tag exists then its contents will be overwritten. Overrides $CAIN_TAG
-u, --username string username for the cassandra connection. Overrides $CAIN_USERNAME (default "cassandra")

```

#### Examples

Backup to AWS S3

```
```shell
cain backup \
-n default \
-l release=cassandra \
-k keyspace \
--dst s3://db-backup/cassandra
```

Backup to Azure Blob Storage
Backup to AWS S3 using specific tag named `before-upgrade`

```shell
cain backup \
-n default \
-l release=cassandra \
-k keyspace \
--dst s3://db-backup/cassandra \
-t before-upgrade
```

Backup to Azure Blob Storage

```shell
cain backup \
-n default \
-l release=cassandra \
Expand All @@ -90,7 +107,7 @@ cain backup \

Backup to Google Cloud Storage

```
```shell
cain backup \
-n default \
-l release=cassandra \
Expand All @@ -101,14 +118,15 @@ cain backup \
### Restore Cassandra backup from cloud storage

Cain performs a restore in the following way:

1. Restore schema if `schema` is specified.
2. Truncate all tables in `keyspace`.
3. Copy files from the specified `src` (under `keyspace/<keyspaceSchemaHash>/tag/`) - restore is only possible for the same keyspace schema.
4. Load new data using `nodetool refresh`.

#### Usage

```
```shell
$ cain restore --help
restore cassandra cluster from cloud storage

Expand All @@ -124,16 +142,17 @@ Flags:
-p, --parallel int number of files to copy in parallel. set this flag to 0 for full parallelism. Overrides $CAIN_PARALLEL (default 1)
-s, --schema string schema version to restore (optional). Overrides $CAIN_SCHEMA
-l, --selector string selector to filter on. Overrides $CAIN_SELECTOR (default "app=cassandra")
--src string source to restore from. Example: s3://bucket/cassandra/namespace/cluster-name. Overrides $CAIN_SRC
--src string source to restore from. Example: s3://bucket/cassandra/namespace/cluster-name. Notice that the src should not end with the forward slash. Overrides $CAIN_SRC
-t, --tag string tag to restore. Overrides $CAIN_TAG
--user-group string user and group who should own restored files. Overrides $CAIN_USER_GROUP (default "cassandra:cassandra")
--user-group string user and group who should own restored files. Overrides $CAIN_USER_GROUP (default "cassandra:cassandra").
If you use rootless containers then ensure to set exactly the same user and group (by name or number) as in target container.
```

#### Examples

Restore from S3

```
```shell
cain restore \
--src s3://db-backup/cassandra/default/ring01
-n default \
Expand All @@ -142,9 +161,20 @@ cain restore \
-t 20180903091624
```

Restore from Azure Blob Storage
Restore from S3 from specific tag used before

```shell
cain restore \
--src s3://db-backup/cassandra/default/ring01
-n default \
-k keyspace \
-l release=cassandra \
-t before-upgrade
```

Restore from Azure Blob Storage

```shell
cain restore \
--src s3://my-account/db-backup-container/cassandra/default/ring01
-n default \
Expand All @@ -155,7 +185,7 @@ cain restore \

Restore from Google Cloud Storage

```
```shell
cain restore \
--src gcs://db-backup/cassandra/default/ring01
-n default \
Expand All @@ -170,7 +200,7 @@ Cain describes the `keyspace` schema using `cqlsh`. It can return the schema its

#### Usage

```
```shell
$ cain schema --help
get schema of cassandra cluster

Expand All @@ -187,13 +217,14 @@ Flags:

#### Examples

```
```shell
cain schema \
-n default \
-l release=cassandra \
-k keyspace
```
```

```shell
cain schema \
-n default \
-l release=cassandra \
Expand All @@ -206,17 +237,17 @@ cain schema \
Cain commands support the usage of environment variables instead of flags. For example:
The `backup` command can be executed as mentioned in the example:

```
```shell
cain backup \
-n default \
-l release=cassandra \
-k keyspace \
--dst s3://db-backup/cassandra
```

You can also set the appropriate envrionment variables (CAIN_FLAG, _ instead of -):
You can also set the appropriate environment variables (`CAIN_FLAG`, `_` instead of `-`):

```
```shell
export CAIN_NAMESPACE=default
export CAIN_SELECTOR=release=cassandra
export CAIN_KEYSPACE=keyspace
Expand Down Expand Up @@ -245,15 +276,14 @@ Since Cain uses [Skbn](https://github.com/maorfr/skbn), adding support for addit

## Credentials


### Kubernetes

Cain tries to get credentials in the following order:

1. if `KUBECONFIG` environment variable is set - cain will use the current context from that config file
2. if `~/.kube/config` exists - cain will use the current context from that config file with an [out-of-cluster client configuration](https://github.com/kubernetes/client-go/tree/master/examples/out-of-cluster-client-configuration)
3. if `~/.kube/config` does not exist - cain will assume it is working from inside a pod and will use an [in-cluster client configuration](https://github.com/kubernetes/client-go/tree/master/examples/in-cluster-client-configuration)


### AWS

Cain uses the default AWS [credentials chain](https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html).
Expand All @@ -264,8 +294,8 @@ Cain uses `AZURE_STORAGE_ACCOUNT` and `AZURE_STORAGE_ACCESS_KEY` environment var

### Google Cloud Storage

Cain uses Google [Application Default Credentials](https://cloud.google.com/docs/authentication/production).
Basically, it will first look for the `GOOGLE_APPLICATION_CREDENTIALS` environment variable. If it is not defined, it will look for the default service account, or throw an error if none is configured.
Cain uses Google [Application Default Credentials](https://cloud.google.com/docs/authentication/production).
Basically, it will first look for the `GOOGLE_APPLICATION_CREDENTIALS` environment variable. If it is not defined, it will look for the default service account, or throw an error if none is configured.

## Examples

Expand Down
3 changes: 3 additions & 0 deletions cmd/cain.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ type backupCmd struct {
cassandraDataDir string
username string
password string
tag string

out io.Writer
}
Expand Down Expand Up @@ -85,6 +86,7 @@ func NewBackupCmd(out io.Writer) *cobra.Command {
CassandraDataDir: b.cassandraDataDir,
Username: b.username,
Password: b.password,
Tag: b.tag,
}
if _, err := cain.Backup(options); err != nil {
log.Fatal(err)
Expand All @@ -103,6 +105,7 @@ func NewBackupCmd(out io.Writer) *cobra.Command {
f.StringVar(&b.cassandraDataDir, "cassandra-data-dir", utils.GetStringEnvVar("CAIN_CASSANDRA_DATA_DIR", "/var/lib/cassandra/data"), "cassandra data directory. Overrides $CAIN_CASSANDRA_DATA_DIR")
f.StringVarP(&b.username, "username", "u", utils.GetStringEnvVar("CAIN_USERNAME", "cassandra"), "username for the cassandra connection. Overrides $CAIN_USERNAME")
f.StringVarP(&b.password, "password", "w", utils.GetStringEnvVar("CAIN_PASSWORD", "cassandra"), "password for the cassandra connection. Overrides $CAIN_PASSWORD")
f.StringVarP(&b.tag, "tag", "t", utils.GetStringEnvVar("CAIN_TAG", ""), "tag to backup, if empty then will use current timestamp. Use with cauthon - if tag exists then its contents will be overwritten. Overrides $CAIN_TAG")

return cmd
}
Expand Down
3 changes: 2 additions & 1 deletion pkg/cain/cain.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type BackupOptions struct {
CassandraDataDir string
Username string
Password string
Tag string
}

// Backup performs backup
Expand Down Expand Up @@ -56,7 +57,7 @@ func Backup(o BackupOptions) (string, error) {
}

log.Println("Taking snapshots")
tag := TakeSnapshots(k8sClient, pods, o.Namespace, o.Container, o.Keyspace, o.Username, o.Password)
tag := TakeSnapshots(k8sClient, pods, o.Namespace, o.Container, o.Keyspace, o.Username, o.Password, o.Tag)

log.Println("Calculating paths. This may take a while...")
fromToPathsAllPods, err := utils.GetFromAndToPathsFromK8s(k8sClient, pods, o.Namespace, o.Container, o.Keyspace, tag, dstBasePath, o.CassandraDataDir)
Expand Down
17 changes: 12 additions & 5 deletions pkg/cain/nodetool.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,31 @@ import (
)

// TakeSnapshots takes a snapshot using nodetool in all pods in parallel
func TakeSnapshots(iClient interface{}, pods []string, namespace, container, keyspace string, username string, password string) string {
func TakeSnapshots(iClient interface{}, pods []string, namespace, container, keyspace string, username string, password string, tag string) string {
k8sClient := iClient.(*skbn.K8sClient)
tag := utils.GetTimeStamp()

// get current time as tag
realtag := utils.GetTimeStamp()
// set custom tag if provided
if len(tag) != 0 {
realtag = tag
}

bwgSize := len(pods)
bwg := utils.NewBoundedWaitGroup(bwgSize)
for _, pod := range pods {
bwg.Add(1)

go func(k8sClient *skbn.K8sClient, namespace, pod, container, keyspace, username, password, tag string) {
if err := takeSnapshot(k8sClient, namespace, pod, container, keyspace, username, password, tag); err != nil {
if err := takeSnapshot(k8sClient, namespace, pod, container, keyspace, username, password, realtag); err != nil {
log.Fatal(err)
}
bwg.Done()
}(k8sClient, namespace, pod, container, keyspace, username, password, tag)
}(k8sClient, namespace, pod, container, keyspace, username, password, realtag)
}
bwg.Wait()

return tag
return realtag
}

// ClearSnapshots clears a snapshot using nodetool in all pods in parallel
Expand Down