Kubernetes natively supports dynamic provisioning for many types of file and block storage, but lacks support for object bucket provisioning. In order to provide a native provisioning of object storage buckets, the concept of Object Bucket Claim (OBC/OB) was introduced in a similar manner to Persistent Volume Claim (PVC/PV)
The lib-bucket-provisioner
repo provides a library implementation and design to unify the implementations:
The operator creates a default storage class with the name pattern <noobaa-namespace>.noobaa.io
which helps to identify the target noobaa deployment since storage classes are non-namespaced.
However the administrator of a noobaa deployment can create additional StorageClasses that refer to a different BucketClass for different data placement policies or reclaim policy, and control its visibility to app-owners using RBAC rules.
For more information see https://github.com/kube-object-storage/lib-bucket-provisioner/blob/master/deploy/storageClass.yaml
Example:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: noobaa.noobaa.io
provisioner: noobaa.noobaa.io/obc
reclaimPolicy: Delete
parameters:
bucketclass: noobaa-default-bucket-class
Applications that require a bucket will create an OBC and refer to a storage class name.
See https://github.com/kube-object-storage/lib-bucket-provisioner/blob/master/deploy/example-claim.yaml
The operator will watch for OBC's and fulfill the claims by create/find existing bucket in NooBaa, and will share a config map and a secret with the application in order to give it all the needed details to work with the bucket.
Example:
noobaa obc create my-bucket-claim -n noobaa --app-namespace my-app
apiVersion: objectbucket.io/v1alpha1
kind: ObjectBucketClaim
metadata:
name: my-bucket-claim
namespace: my-app
spec:
generateBucketName: my-bucket
storageClassName: noobaa.noobaa.io
Applications that require a bucket from a specific BucketClass can create an OBC with the default StorageClass, but override the BucketClass specifically for that claim using the spec.additionalConfig.bucketclass
property. The BucketClass can exist either in the same namespace as that of OBC or it can exist in NooBaa system namespace. In case of a conflict, BucketClass that exists in the OBC namespace will take priority over the BucketClass in NooBaa system.
Example:
noobaa obc create my-bucket-claim -n noobaa --app-namespace my-app --bucketclass custom-bucket-class
apiVersion: objectbucket.io/v1alpha1
kind: ObjectBucketClaim
metadata:
name: my-bucket-claim
namespace: my-app
spec:
generateBucketName: my-bucket
storageClassName: noobaa.noobaa.io
additionalConfig:
bucketclass: custom-bucket-class
Applications that require a bucket to have a specific replication policy can create an OBC and add to the claim
the spec.additionalConfig.replication-policy
property.
/path/to/json-file.json is the path to a JSON file which defines the replication policy
Example:
noobaa obc create my-bucket-claim -n noobaa --app-namespace my-app --replication-policy /path/to/json-file.json
apiVersion: objectbucket.io/v1alpha1
kind: ObjectBucketClaim
metadata:
name: my-bucket-claim
namespace: my-app
spec:
generateBucketName: my-bucket
storageClassName: noobaa.noobaa.io
additionalConfig:
replicationPolicy: [{ "rule_id": "rule-2", "destination_bucket": "first.bucket", "filter": {"prefix": "bc"}}]
It is possible to create an OBC on top of NSFS. In order to do this, two prerequisites need to be provided:
- The name of a bucketclass that was created on top of an NSFS namespacestore
- Filesystem access mapping (either
uid
andgid
, or adistinguished_name
)
In the case of applying a YAML, the user has to provide the data as a JSON object in nsfsAccountConfig
under the OBC's spec.additionalConfig
key (see example below)
Both CLI and YAML options utilize the same parameter names: gid - the group ID of the account that should be mimicked within the filesystem uid - the user ID of the account that should be mimicked within the filesystem OR distinguished_name - the distinguished name of the account that should be mimicked within the filesystem
These optional additionalConfig keys can be provided by the user to further configure NSFS OBC properties: path - the filesystem path that should be 'mounted' as the bucket's root. Please make sure that the path was created with the appropriate ownership and permissions ahead of time.
Examples:
noobaa obc create my-bucket-claim -n my-app --app-namespace my-app --bucketclass nsfs-bucket-class --uid 42 --gid 505 --path '/mnt/nsfs'
apiVersion: objectbucket.io/v1alpha1
kind: ObjectBucketClaim
metadata:
name: my-bucket-claim
namespace: my-app
spec:
generateBucketName: my-bucket
storageClassName: noobaa.noobaa.io
additionalConfig:
bucketclass: nsfs-bucket-class
nsfsAccountConfig: { "distinguished_name": "current_user" }
path: "/mnt/nsfs"
Once the OBC is provisioned by the operator, a bucket will be created in NooBaa, and the operator will create a Secret and ConfigMap with the same name of the OBC on the same namespace of the OBC. For the example above, the Secret and ConfigMap will both be named my-bucket-claim
.
The content of the Secret and ConfigMap provides all the information needed by the application in order to connect to the bucket using S3 API, and these can be mounted into the application pods using env or volumes. The application should configure its S3 SDK to use the AWS_ACCESS_KEY_ID & AWS_SECRET_ACCESS_KEY credentials as provided by the Secret, and the BUCKET_HOST:BUCKET_PORT endpoint and BUCKET_NAME as provided by the ConfigMap - see below.
NOTE on SSL certificates:
In the last release v2.0.9 the BUCKET_HOST:BUCKET_PORT refers to the s3 service ClusterIP and the https port. However, the SSL certificate of the service is self signed by the cluster CA, and whenever a client application running in the cluster wants to use it and trust the cluster CA it should use the service name instead, which is s3.<noobaa-namespace>.svc.cluster.local
with the default 443 https port. This was fixed in #189 and will be part of v2.1.0 once it gets released.
Here is an example content:
Secret:
apiVersion: v1
data:
AWS_ACCESS_KEY_ID: UU1odkFSWjNiMWplTmx2UnZpcHU=
AWS_SECRET_ACCESS_KEY: EXAMPLEeTdFdlkva1FSQVEzWElWWXB3L2ZYdVBZdG8wSDBxaVlBSUpraW1iTA==
kind: Secret
metadata:
creationTimestamp: "2020-01-14T08:05:31Z"
finalizers:
- objectbucket.io/finalizer
labels:
app: noobaa
bucket-provisioner: noobaa.noobaa.io-obc
noobaa-domain: noobaa.noobaa.io
name: my-bucket-claim
namespace: my-app
ownerReferences:
- apiVersion: objectbucket.io/v1alpha1
blockOwnerDeletion: true
controller: true
kind: ObjectBucketClaim
name: my-bucket-claim
uid: a2852724-36a4-11ea-bb22-0a49da05a082
resourceVersion: "24953844"
selfLink: /api/v1/namespaces/my-app/secrets/my-bucket-claim
uid: a2a6ee13-36a4-11ea-bb22-0a49da05a082
type: Opaque
ConfigMap:
apiVersion: v1
data:
BUCKET_HOST: 10.0.169.118
BUCKET_NAME: my-bucket-9febb742-f14d-4a94-8ed8-3da5d8bd242c
BUCKET_PORT: "32603"
BUCKET_REGION: ""
BUCKET_SUBREGION: ""
kind: ConfigMap
metadata:
creationTimestamp: "2020-01-14T08:05:32Z"
finalizers:
- objectbucket.io/finalizer
labels:
app: noobaa
bucket-provisioner: noobaa.noobaa.io-obc
noobaa-domain: noobaa.noobaa.io
name: my-bucket-claim
namespace: my-app
ownerReferences:
- apiVersion: objectbucket.io/v1alpha1
blockOwnerDeletion: true
controller: true
kind: ObjectBucketClaim
name: my-bucket-claim
uid: a2852724-36a4-11ea-bb22-0a49da05a082
resourceVersion: "24953845"
selfLink: /api/v1/namespaces/my-app/configmaps/my-bucket-claim
uid: a2b28adc-36a4-11ea-bb22-0a49da05a082
An application deployment can claim a bucket and refer to the expected Secret & ConfigMap in a static deployment yaml, since the names of the Secret and ConfigMap follow the same name as the OBC, and pods that mount information from Secret or ConfigMaps will not start until those resources exist, this provides a self contained deployment that will only run the application once the bucket provisioning is complete.
Here is an example yaml that combines the OBC and a Pod that uses it:
apiVersion: objectbucket.io/v1alpha1
kind: ObjectBucketClaim
metadata:
name: my-bucket-claim
spec:
generateBucketName: my-bucket
storageClassName: noobaa.noobaa.io
---
apiVersion: v1
kind: Pod
metadata:
name: my-pod
labels:
app: my-app
spec:
containers:
- name: app
envFrom:
- secretRef:
name: my-bucket-claim
- configMapRef:
name: my-bucket-claim
image: banst/awscli
command:
- sh
- "-c"
- |
echo "----> Configuring S3 endpoint ...";
pip install awscli-plugin-endpoint;
aws configure set plugins.endpoint awscli_plugin_endpoint;
aws configure set s3.endpoint_url https://s3.noobaa.svc.cluster.local;
echo "----> Configuring certificates ...";
aws configure set ca_bundle /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt;
echo "----> Copying files ...";
aws s3 cp --recursive /etc s3://$BUCKET_NAME;
echo "----> List files ...";
aws s3 ls $BUCKET_NAME;
echo "----> Done.";
The scope of bucket permissions is at the claim scope - this means that the credentials of the OBC are confined to access only that single OBC bucket. Notice that also listing buckets with these S3 credentials will return only that one bucket.
Going forward we would like to have an option to create a single account per application namespace so that all buckets claimed by an application will be visible and accessible to that application.