Object storage¶
Every organization with a Kubernetes cluster holds one bucket. Public
Research organizations have none. The bucket is the durable store for
datasets, checkpoints and outputs. It outlives every cluster. It is
reachable from every site and from outside the platform through an S3
compatible endpoint. One access key pair opens it. The platform writes
that pair into every Kubernetes cluster of the organization as the
Secret nc-storage in the default namespace. The console's
Storage page shows the same pair on request.
The bucket¶
The platform creates the bucket at the organization's first
Kubernetes cluster. The name is assigned by the platform (nc- and 16
hex characters). The Storage page shows the bucket under Object
storage on every cluster of the organization, with the endpoint, the
bucket's name and the access key ID. An organization whose clusters
predate object storage and whose Storage page shows no Object
storage block asks support.
The bucket belongs to the organization. Nothing about it is per cluster: every Kubernetes cluster of the organization, on every site, reaches the same bucket with the same pair. The bucket survives cluster deletion. Deleting the organization's last cluster does not delete it.
Slurm clusters and VM clusters receive no Secret (below).
The credential¶
One access key pair per organization: an access key ID and a secret access key. Every member of the organization shares it. There are no per member credentials, no per cluster credentials and no prefix scoping. The pair grants read and write on the whole bucket.
The Secret nc-storage¶
The platform writes the pair into every Kubernetes cluster of the
organization as the Secret nc-storage in the default namespace,
within minutes of a cluster's creation and again at every rotate. The
Secret carries five keys, named so that the common S3 clients read them
with no mapping:
| Key | Value |
|---|---|
AWS_ACCESS_KEY_ID |
the access key ID |
AWS_SECRET_ACCESS_KEY |
the secret access key |
AWS_ENDPOINT_URL |
the S3 compatible endpoint |
AWS_DEFAULT_REGION |
auto |
NC_BUCKET |
the bucket's name |
The Secret is platform managed (label
app.kubernetes.io/managed-by: nationalcompute-station). An edit to it
is overwritten at the next push. Deleting it revokes nothing. The
platform writes it again at the next rotate. The Secret exists in the
default namespace only. A pod in another namespace cannot reference
it. Copy it into that namespace yourself. A copy does not follow a
rotate.
The Storage page shows the Secret's state on the picked cluster: present once the write landed, pending with a short reason while it has not, and Kubernetes clusters only on a Slurm or VM cluster. The platform retries a pending write after five minutes at first and then at growing intervals up to six hours. A Secret that stays pending is a support matter.
Show and Rotate on the Storage page¶
Show credentials displays the pair. Each display is recorded in your organization's activity trail with the member who asked. Rotate credentials replaces it (below). Both are console actions. No API token and no agent tool can show or rotate the pair.
Using the bucket from a pod¶
envFrom the Secret. The five variables land in the container's
environment. boto3 and the AWS CLI read them as they are: no endpoint
flag, no credentials file, no region to choose.
apiVersion: batch/v1
kind: Job
metadata:
name: bucket-smoke
spec:
backoffLimit: 0
ttlSecondsAfterFinished: 120
template:
spec:
restartPolicy: Never
containers:
- name: aws
image: public.ecr.aws/aws-cli/aws-cli:2.38.5
envFrom:
- secretRef:
name: nc-storage
env:
# the endpoint does not implement the default checksum mode
# of current AWS CLI and boto3 releases
- { name: AWS_REQUEST_CHECKSUM_CALCULATION, value: when_required }
- { name: AWS_RESPONSE_CHECKSUM_VALIDATION, value: when_required }
command: ["sh", "-c"]
args:
- |
echo hello > /tmp/hello
aws s3 cp /tmp/hello "s3://$NC_BUCKET/smoke/hello"
aws s3 ls "s3://$NC_BUCKET/smoke/"
The Job requests no GPUs, so it runs at once on the
cluster CPU worker and never
touches the market. A GPU job uses the same envFrom block.
The same round trip in Python:
import os
import boto3
s3 = boto3.client("s3") # endpoint, region and keys come from the environment
bucket = os.environ["NC_BUCKET"]
s3.put_object(Bucket=bucket, Key="smoke/hello", Body=b"hello\n")
print(s3.get_object(Bucket=bucket, Key="smoke/hello")["Body"].read())
The two checksum variables in the Job apply to boto3 as well. An
older client release that does not read AWS_ENDPOINT_URL takes the
endpoint explicitly: --endpoint-url "$AWS_ENDPOINT_URL" on the AWS
CLI, endpoint_url=os.environ["AWS_ENDPOINT_URL"] on a boto3 client.
Multipart uploads work as on any S3 endpoint. An object is currently
at most 5 TiB. A key accepts one write per second. A second write to
the same key within that second fails. Give concurrent writers their
own keys.
The pair works from anywhere on the internet with any S3 client: a laptop, a VM node, a Slurm login node, a Marshall workspace. Set the five variables from the Storage page's Show credentials and the same commands run.
The starter recipes use the same envFrom block, with
optional: true so the files run on a cluster without the Secret:
object storage recipes.
Rotation¶
Rotate credentials on the Storage page replaces the whole pair. Both halves change: the access key ID and the secret access key. The platform writes the new pair into every Kubernetes cluster of the organization and shows it once.
- The old pair keeps working for ten minutes after the rotate, then stops. Every copy you saved stops working with it at that point.
- The Secret
nc-storageis updated in every Kubernetes cluster of the organization within seconds. A cluster the write cannot reach reads pending on the Storage page and receives the pair when the platform's retry lands. - A pod that loaded the Secret through
envFromkeeps the old pair until it restarts. Kubernetes injects the environment at container start and never updates it. Restart the consuming pods within the ten minutes after a rotate. A pod that starts in the seconds between the rotate and the update on its cluster loads the old pair too. A volume mount of the Secret refreshes on its own within about a minute.
Rotate when a member leaves the organization and after any suspected exposure of the pair. Member removal revokes that member's tokens and sign in. It does not change the bucket pair. A departed member may hold a copy. The activity trail records who pressed Show on the console. It is not a record of who holds the pair: any member, workload or agent account in a cluster reads the Secret without a record.
The soft cap¶
Each bucket carries a soft cap, currently 2 TiB. Usage is sampled hourly. The Storage page shows the bytes and objects held against the cap. Over the cap the page shows a warning and asks you to contact support, who raise the cap on request. Nothing is refused at the cap today: writes continue while the warning shows. The bucket is not billed today.
Reading the bucket by API¶
GET /api/k8s/storage/bucket with an org API token answers the
bucket's facts, its usage against the cap and the Secret's state on
each Kubernetes cluster of the organization. It never carries the
secret access key
(limit price API). On the
MCP server the same body is grid_api get /api/k8s/storage/bucket.
Show and Rotate have no route and no tool.
What the bucket is not¶
- No public access. There is no public URL, no anonymous read and no per object sharing link. Whoever holds the pair reads and writes. Nobody else does.
- One pair. The pair opens the whole bucket for the whole organization. Scoping by member, by cluster or by prefix does not exist.
- No versioning and no lifecycle rules. The platform keeps no object versions, sets no expiry rules and takes no backups of the bucket. A deleted object is gone.
- Kubernetes clusters only. The Secret lands in Kubernetes clusters. Slurm clusters and VM clusters receive nothing. The pair itself works from any machine.
- No tenant delete. No console action and no route deletes the bucket or revokes its pair. Ask support. A bucket is destroyed only when it is empty.
- No append and no tail. A write replaces the whole object. A log kept in the bucket reads as of its last upload (logs in the bucket).
- No POSIX mount. For a filesystem shared by every node of a cluster, ask support for a shared volume.