MinIO Object Storage on Kubernetes: Enterprise Implementation Guide
MinIO provides high-performance, S3-compatible object storage that’s perfect for cloud-native applications on Kubernetes. This comprehensive guide covers enterprise deployment patterns, multi-tenancy configuration, security hardening, and performance optimization for production MinIO clusters.
MinIO Object Storage on Kubernetes: Enterprise Implementation Guide
Executive Summary
MinIO is a high-performance, distributed object storage system designed for cloud-native applications. It provides S3-compatible APIs, making it an ideal replacement for AWS S3 in private cloud and hybrid deployments. This guide covers production-grade deployment on Kubernetes, including high availability, multi-tenancy, security, and performance optimization strategies used in enterprise environments handling petabytes of data.
Architecture Overview
MinIO Deployment Patterns
# minio-architecture.yaml
# Distributed MinIO with 4 nodes, 4 drives per node
---
apiVersion: v1
kind: Namespace
metadata:
name: minio-system
---
# MinIO Operator - Manages MinIO tenants
apiVersion: v1
kind: ServiceAccount
metadata:
name: minio-operator
namespace: minio-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: minio-operator
rules:
- apiGroups: [""]
resources: ["pods", "services", "endpoints", "persistentvolumeclaims", "events", "configmaps", "secrets"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: ["apps"]
resources: ["deployments", "statefulsets", "replicasets"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: ["minio.min.io"]
resources: ["tenants"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: minio-operator
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: minio-operator
subjects:
- kind: ServiceAccount
name: minio-operator
namespace: minio-system
---
# MinIO Operator Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: minio-operator
namespace: minio-system
spec:
replicas: 2
selector:
matchLabels:
app: minio-operator
template:
metadata:
labels:
app: minio-operator
spec:
serviceAccountName: minio-operator
containers:
- name: minio-operator
image: minio/operator:v5.0.11
imagePullPolicy: IfNotPresent
args:
- controller
env:
- name: CLUSTER_DOMAIN
value: "cluster.local"
- name: WATCHED_NAMESPACE
value: "" # Watch all namespaces
resources:
requests:
cpu: 200m
memory: 256Mi
limits:
cpu: 500m
memory: 512Mi
securityContext:
runAsNonRoot: true
runAsUser: 1000
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
Production MinIO Tenant Deployment
High-Availability Tenant Configuration
# minio-tenant-production.yaml
apiVersion: minio.min.io/v2
kind: Tenant
metadata:
name: production
namespace: minio-tenants
labels:
app: minio
environment: production
spec:
# Image configuration
image: minio/minio:RELEASE.2024-01-01T00-00-00Z
imagePullPolicy: IfNotPresent
# Distributed configuration: 4 servers × 4 drives = 16 total drives
# Provides N/2 write and read quorum (can survive 8 drive failures)
pools:
- servers: 4
name: pool-0
volumesPerServer: 4
volumeClaimTemplate:
metadata:
name: data
spec:
storageClassName: fast-ssd
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Ti
# Resource allocation per server
resources:
requests:
cpu: 4
memory: 8Gi
limits:
cpu: 8
memory: 8Gi
# Security context
securityContext:
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
runAsNonRoot: true
# Node affinity for distribution
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: v1.min.io/tenant
operator: In
values:
- production
- key: v1.min.io/pool
operator: In
values:
- pool-0
topologyKey: kubernetes.io/hostname
# Enable TLS for all connections
requestAutoCert: true
# Certificate configuration
externalCertSecret:
- name: minio-tls
type: kubernetes.io/tls
# MinIO environment variables
env:
- name: MINIO_BROWSER
value: "on"
- name: MINIO_PROMETHEUS_AUTH_TYPE
value: "public"
- name: MINIO_UPDATE
value: "off" # Disable auto-updates in production
- name: MINIO_STORAGE_CLASS_STANDARD
value: "EC:2" # Erasure coding configuration
# Features
features:
bucketDNS: true
domains:
minio:
- "minio.example.com"
console:
- "console.minio.example.com"
# Console configuration
console:
image: minio/console:v0.24.0
replicas: 2
consoleSecret:
name: console-secret
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
cpu: 1
memory: 1Gi
# Prometheus metrics
prometheusOperator: true
# Logging
logging:
anonymous: false
json: true
quiet: false
# Service configuration
serviceMetadata:
minioServiceLabels:
app: minio
minioServiceAnnotations:
prometheus.io/scrape: "true"
prometheus.io/port: "9000"
prometheus.io/path: "/minio/v2/metrics/cluster"
# Users - create default admin user
users:
- name: admin-user
---
# Admin user credentials
apiVersion: v1
kind: Secret
metadata:
name: admin-user
namespace: minio-tenants
type: Opaque
stringData:
CONSOLE_ACCESS_KEY: admin
CONSOLE_SECRET_KEY: changeme123456 # Change in production!
---
# Console secret
apiVersion: v1
kind: Secret
metadata:
name: console-secret
namespace: minio-tenants
type: Opaque
stringData:
CONSOLE_PBKDF_PASSPHRASE: "changeme-console-passphrase"
CONSOLE_PBKDF_SALT: "changeme-console-salt"
---
# TLS certificate (use cert-manager in production)
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: minio-tls
namespace: minio-tenants
spec:
secretName: minio-tls
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
dnsNames:
- minio.example.com
- "*.minio.example.com"
- "*.minio-tenants.svc.cluster.local"
---
# Ingress for MinIO API
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: minio-api
namespace: minio-tenants
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/proxy-body-size: "0"
nginx.ingress.kubernetes.io/proxy-buffering: "off"
nginx.ingress.kubernetes.io/proxy-request-buffering: "off"
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
spec:
ingressClassName: nginx
tls:
- hosts:
- minio.example.com
secretName: minio-api-tls
rules:
- host: minio.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: minio
port:
number: 443
---
# Ingress for MinIO Console
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: minio-console
namespace: minio-tenants
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/proxy-body-size: "0"
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
spec:
ingressClassName: nginx
tls:
- hosts:
- console.minio.example.com
secretName: minio-console-tls
rules:
- host: console.minio.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: production-console
port:
number: 9443
Multi-Tenancy Configuration
Tenant Isolation and Management
#!/usr/bin/env python3
"""
MinIO tenant management and provisioning
"""
import boto3
from botocore.client import Config
from typing import Dict, List, Optional
import json
import secrets
import string
class MinIOTenantManager:
"""Manage MinIO tenants, users, and policies"""
def __init__(self, endpoint_url: str, access_key: str, secret_key: str):
"""Initialize MinIO admin client"""
self.endpoint = endpoint_url
self.admin_client = boto3.client(
's3',
endpoint_url=endpoint_url,
aws_access_key_id=access_key,
aws_secret_access_key=secret_key,
config=Config(signature_version='s3v4'),
verify=True
)
def create_tenant(self, tenant_name: str, quota_gb: int = 100) -> Dict:
"""
Create a new tenant with isolated namespace
In MinIO, a tenant is implemented as a user with specific policies
"""
# Generate credentials
access_key = f"tenant-{tenant_name}-{''.join(secrets.choice(string.ascii_lowercase) for _ in range(8))}"
secret_key = self._generate_secret_key()
# Create user
user_info = self._create_user(access_key, secret_key)
# Create tenant bucket prefix
bucket_prefix = f"tenant-{tenant_name}"
# Create policy for tenant
policy = self._create_tenant_policy(tenant_name, bucket_prefix, quota_gb)
policy_name = f"policy-{tenant_name}"
self._attach_policy(policy_name, policy, access_key)
return {
'tenant_name': tenant_name,
'access_key': access_key,
'secret_key': secret_key,
'bucket_prefix': bucket_prefix,
'quota_gb': quota_gb,
'policy_name': policy_name
}
def _generate_secret_key(self, length: int = 40) -> str:
"""Generate secure secret key"""
alphabet = string.ascii_letters + string.digits
return ''.join(secrets.choice(alphabet) for _ in range(length))
def _create_user(self, access_key: str, secret_key: str) -> Dict:
"""Create MinIO user using mc admin user add"""
# This would use MinIO Admin API or mc command
# For demonstration, showing the structure
return {
'accessKey': access_key,
'status': 'enabled'
}
def _create_tenant_policy(self, tenant_name: str, bucket_prefix: str, quota_gb: int) -> str:
"""Create IAM policy for tenant isolation"""
policy = {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:ListBucket",
"s3:GetBucketLocation",
"s3:ListBucketMultipartUploads"
],
"Resource": [
f"arn:aws:s3:::{bucket_prefix}-*"
],
"Condition": {
"NumericLessThanEquals": {
"s3:BucketSize": quota_gb * 1024 * 1024 * 1024
}
}
},
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject",
"s3:GetObjectVersion",
"s3:DeleteObjectVersion",
"s3:AbortMultipartUpload",
"s3:ListMultipartUploadParts"
],
"Resource": [
f"arn:aws:s3:::{bucket_prefix}-*/*"
]
},
{
"Effect": "Allow",
"Action": [
"s3:CreateBucket",
"s3:DeleteBucket",
"s3:PutBucketPolicy",
"s3:GetBucketPolicy",
"s3:DeleteBucketPolicy"
],
"Resource": [
f"arn:aws:s3:::{bucket_prefix}-*"
]
},
{
"Effect": "Deny",
"Action": [
"s3:*"
],
"NotResource": [
f"arn:aws:s3:::{bucket_prefix}-*",
f"arn:aws:s3:::{bucket_prefix}-*/*"
]
}
]
}
return json.dumps(policy, indent=2)
def _attach_policy(self, policy_name: str, policy: str, user: str):
"""Attach policy to user"""
# This would use MinIO Admin API
# mc admin policy add myminio policy-name policy.json
# mc admin policy set myminio policy-name user=username
pass
def create_tenant_bucket(self, tenant_info: Dict, bucket_name: str,
versioning: bool = True,
encryption: bool = True) -> Dict:
"""Create a bucket for tenant with configuration"""
full_bucket_name = f"{tenant_info['bucket_prefix']}-{bucket_name}"
# Create bucket using tenant credentials
tenant_client = boto3.client(
's3',
endpoint_url=self.endpoint,
aws_access_key_id=tenant_info['access_key'],
aws_secret_access_key=tenant_info['secret_key'],
config=Config(signature_version='s3v4')
)
# Create bucket
tenant_client.create_bucket(Bucket=full_bucket_name)
# Enable versioning
if versioning:
tenant_client.put_bucket_versioning(
Bucket=full_bucket_name,
VersioningConfiguration={'Status': 'Enabled'}
)
# Enable encryption
if encryption:
tenant_client.put_bucket_encryption(
Bucket=full_bucket_name,
ServerSideEncryptionConfiguration={
'Rules': [{
'ApplyServerSideEncryptionByDefault': {
'SSEAlgorithm': 'AES256'
},
'BucketKeyEnabled': True
}]
}
)
# Set lifecycle policy
lifecycle_policy = {
'Rules': [
{
'ID': 'DeleteOldVersions',
'Status': 'Enabled',
'NoncurrentVersionExpiration': {
'NoncurrentDays': 90
}
},
{
'ID': 'AbortIncompleteMultipartUpload',
'Status': 'Enabled',
'AbortIncompleteMultipartUpload': {
'DaysAfterInitiation': 7
}
}
]
}
tenant_client.put_bucket_lifecycle_configuration(
Bucket=full_bucket_name,
LifecycleConfiguration=lifecycle_policy
)
return {
'bucket_name': full_bucket_name,
'versioning': versioning,
'encryption': encryption,
'endpoint': self.endpoint
}
def set_bucket_quota(self, bucket_name: str, quota_bytes: int):
"""Set quota for a bucket using mc admin"""
# mc admin bucket quota myminio/bucket --hard 10GiB
pass
def get_tenant_usage(self, tenant_info: Dict) -> Dict:
"""Get storage usage for tenant"""
tenant_client = boto3.client(
's3',
endpoint_url=self.endpoint,
aws_access_key_id=tenant_info['access_key'],
aws_secret_access_key=tenant_info['secret_key'],
config=Config(signature_version='s3v4')
)
prefix = tenant_info['bucket_prefix']
total_size = 0
total_objects = 0
buckets = []
# List all tenant buckets
response = tenant_client.list_buckets()
for bucket in response['Buckets']:
if bucket['Name'].startswith(prefix):
bucket_name = bucket['Name']
# Get bucket size
paginator = tenant_client.get_paginator('list_objects_v2')
pages = paginator.paginate(Bucket=bucket_name)
bucket_size = 0
bucket_objects = 0
for page in pages:
if 'Contents' in page:
for obj in page['Contents']:
bucket_size += obj['Size']
bucket_objects += 1
total_size += bucket_size
total_objects += bucket_objects
buckets.append({
'name': bucket_name,
'size_bytes': bucket_size,
'size_gb': bucket_size / (1024**3),
'objects': bucket_objects
})
return {
'tenant_name': tenant_info['tenant_name'],
'total_size_bytes': total_size,
'total_size_gb': total_size / (1024**3),
'total_objects': total_objects,
'quota_gb': tenant_info['quota_gb'],
'usage_percent': (total_size / (1024**3)) / tenant_info['quota_gb'] * 100,
'buckets': buckets
}
# Example usage
def main():
"""Example tenant management"""
manager = MinIOTenantManager(
endpoint_url='https://minio.example.com',
access_key='admin',
secret_key='admin-secret-key'
)
# Create tenant
print("Creating tenant...")
tenant = manager.create_tenant(
tenant_name='acme-corp',
quota_gb=1000
)
print(f"Tenant created: {tenant['tenant_name']}")
print(f"Access Key: {tenant['access_key']}")
print(f"Secret Key: {tenant['secret_key']}")
# Create bucket for tenant
print("\nCreating bucket...")
bucket = manager.create_tenant_bucket(
tenant_info=tenant,
bucket_name='documents',
versioning=True,
encryption=True
)
print(f"Bucket created: {bucket['bucket_name']}")
# Get usage
print("\nGetting tenant usage...")
usage = manager.get_tenant_usage(tenant)
print(f"Total size: {usage['total_size_gb']:.2f} GB")
print(f"Total objects: {usage['total_objects']}")
print(f"Usage: {usage['usage_percent']:.2f}%")
if __name__ == "__main__":
main()
Encryption and Security
Server-Side Encryption with KMS
# minio-kms-encryption.yaml
---
# HashiCorp Vault integration for key management
apiVersion: v1
kind: Secret
metadata:
name: minio-kms-config
namespace: minio-tenants
type: Opaque
stringData:
config.yaml: |
version: v1
address: 0.0.0.0:7373
admin:
identity: "" # Optional admin identity
# TLS configuration
tls:
key: /tmp/kes/server.key
cert: /tmp/kes/server.cert
# Policy configuration
policy:
minio-server:
allow:
- /v1/key/create/*
- /v1/key/generate/*
- /v1/key/decrypt/*
identities:
- ${MINIO_KES_IDENTITY}
# Key store - HashiCorp Vault
keystore:
vault:
endpoint: https://vault.vault.svc.cluster.local:8200
namespace: "" # Vault namespace if using Vault Enterprise
prefix: minio
approle:
id: ${VAULT_APPROLE_ID}
secret: ${VAULT_APPROLE_SECRET}
retry: 15s
status:
ping: 10s
# TLS configuration for Vault
tls:
ca: /tmp/vault/ca.crt
---
# KES (Key Encryption Service) Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: minio-kes
namespace: minio-tenants
spec:
replicas: 2
selector:
matchLabels:
app: minio-kes
template:
metadata:
labels:
app: minio-kes
spec:
containers:
- name: kes
image: minio/kes:2024-01-01T00-00-00Z
args:
- server
- --config=/etc/kes/config.yaml
- --auth=off # Using policy-based auth instead
env:
- name: MINIO_KES_IDENTITY
valueFrom:
secretKeyRef:
name: minio-kes-identity
key: identity
- name: VAULT_APPROLE_ID
valueFrom:
secretKeyRef:
name: vault-approle
key: role-id
- name: VAULT_APPROLE_SECRET
valueFrom:
secretKeyRef:
name: vault-approle
key: secret-id
ports:
- containerPort: 7373
name: https
protocol: TCP
volumeMounts:
- name: kes-config
mountPath: /etc/kes
readOnly: true
- name: kes-tls
mountPath: /tmp/kes
readOnly: true
- name: vault-ca
mountPath: /tmp/vault
readOnly: true
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
cpu: 1
memory: 1Gi
livenessProbe:
httpGet:
path: /v1/status
port: 7373
scheme: HTTPS
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /v1/status
port: 7373
scheme: HTTPS
initialDelaySeconds: 10
periodSeconds: 5
volumes:
- name: kes-config
secret:
secretName: minio-kms-config
- name: kes-tls
secret:
secretName: kes-tls-cert
- name: vault-ca
secret:
secretName: vault-ca-cert
---
# KES Service
apiVersion: v1
kind: Service
metadata:
name: minio-kes
namespace: minio-tenants
spec:
selector:
app: minio-kes
ports:
- port: 7373
targetPort: 7373
protocol: TCP
name: https
type: ClusterIP
---
# MinIO tenant with KES encryption
apiVersion: minio.min.io/v2
kind: Tenant
metadata:
name: encrypted-tenant
namespace: minio-tenants
spec:
# ... (other configuration as before)
# KES configuration for encryption
kes:
image: minio/kes:2024-01-01T00-00-00Z
replicas: 2
kesSecret:
name: minio-kes-config
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
cpu: 1
memory: 1Gi
# Environment for KES
env:
- name: MINIO_KMS_KES_ENDPOINT
value: https://minio-kes:7373
- name: MINIO_KMS_KES_KEY_NAME
value: minio-default-key
- name: MINIO_KMS_KES_CERT_FILE
value: /tmp/kes/client.cert
- name: MINIO_KMS_KES_KEY_FILE
value: /tmp/kes/client.key
- name: MINIO_KMS_KES_CA_PATH
value: /tmp/kes/ca.cert
Bucket Encryption Policy
#!/usr/bin/env python3
"""
MinIO encryption management
"""
import boto3
from botocore.client import Config
import json
class MinIOEncryptionManager:
"""Manage MinIO bucket encryption"""
def __init__(self, endpoint: str, access_key: str, secret_key: str):
self.client = boto3.client(
's3',
endpoint_url=endpoint,
aws_access_key_id=access_key,
aws_secret_access_key=secret_key,
config=Config(signature_version='s3v4')
)
def enable_default_encryption(self, bucket: str, algorithm: str = 'AES256'):
"""
Enable default encryption for bucket
algorithm: 'AES256' or 'aws:kms'
"""
encryption_config = {
'Rules': [{
'ApplyServerSideEncryptionByDefault': {
'SSEAlgorithm': algorithm
},
'BucketKeyEnabled': True
}]
}
if algorithm == 'aws:kms':
encryption_config['Rules'][0]['ApplyServerSideEncryptionByDefault']['KMSMasterKeyID'] = 'minio-default-key'
self.client.put_bucket_encryption(
Bucket=bucket,
ServerSideEncryptionConfiguration=encryption_config
)
print(f"Enabled {algorithm} encryption for bucket: {bucket}")
def enforce_encryption_policy(self, bucket: str):
"""
Enforce encryption via bucket policy
Deny any upload without encryption
"""
policy = {
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyUnencryptedObjectUploads",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:PutObject",
"Resource": f"arn:aws:s3:::{bucket}/*",
"Condition": {
"StringNotEquals": {
"s3:x-amz-server-side-encryption": [
"AES256",
"aws:kms"
]
}
}
}
]
}
self.client.put_bucket_policy(
Bucket=bucket,
Policy=json.dumps(policy)
)
print(f"Enforced encryption policy for bucket: {bucket}")
def upload_encrypted_object(self, bucket: str, key: str, data: bytes,
encryption: str = 'AES256',
kms_key_id: str = None):
"""Upload object with encryption"""
extra_args = {
'ServerSideEncryption': encryption
}
if encryption == 'aws:kms' and kms_key_id:
extra_args['SSEKMSKeyId'] = kms_key_id
self.client.put_object(
Bucket=bucket,
Key=key,
Body=data,
**extra_args
)
print(f"Uploaded encrypted object: {bucket}/{key}")
def verify_encryption(self, bucket: str, key: str) -> dict:
"""Verify object encryption status"""
response = self.client.head_object(
Bucket=bucket,
Key=key
)
encryption_info = {
'encrypted': 'ServerSideEncryption' in response,
'algorithm': response.get('ServerSideEncryption'),
'kms_key_id': response.get('SSEKMSKeyId'),
'bucket_key_enabled': response.get('BucketKeyEnabled', False)
}
return encryption_info
# Example usage
def main():
manager = MinIOEncryptionManager(
endpoint='https://minio.example.com',
access_key='your-access-key',
secret_key='your-secret-key'
)
bucket = 'encrypted-data'
# Enable default encryption
manager.enable_default_encryption(bucket, algorithm='aws:kms')
# Enforce encryption policy
manager.enforce_encryption_policy(bucket)
# Upload encrypted object
manager.upload_encrypted_object(
bucket=bucket,
key='sensitive-data.txt',
data=b'This is sensitive data',
encryption='aws:kms',
kms_key_id='minio-default-key'
)
# Verify encryption
info = manager.verify_encryption(bucket, 'sensitive-data.txt')
print(f"Encryption info: {info}")
if __name__ == "__main__":
main()
Performance Optimization
Erasure Coding Configuration
#!/bin/bash
# minio-erasure-coding.sh
# Erasure coding provides data redundancy and availability
# Format: EC:K (K data shards, M parity shards where M = N - K)
# Examples for different configurations:
# 4 nodes, 4 drives each = 16 drives total
# EC:8 = 8 data + 8 parity (50% storage overhead, can lose 8 drives)
# EC:10 = 10 data + 6 parity (60% usable, can lose 6 drives)
# EC:12 = 12 data + 4 parity (75% usable, can lose 4 drives)
# Set default storage class
export MINIO_STORAGE_CLASS_STANDARD="EC:8"
export MINIO_STORAGE_CLASS_RRS="EC:2" # Reduced redundancy
# Performance vs redundancy tradeoff:
# - Higher data shards (EC:12) = better performance, less redundancy
# - Higher parity shards (EC:4) = more redundancy, more overhead
# Recommended configurations:
# - Critical data: EC:8 (50% overhead, high redundancy)
# - Standard data: EC:10 (40% overhead, good balance)
# - Temporary data: EC:12 (25% overhead, minimal redundancy)
cat > /tmp/storage-class-config.json <<EOF
{
"standard": "EC:8",
"reduced_redundancy": "EC:2"
}
EOF
# Apply via mc admin config
mc admin config set myminio storage_class \
standard="EC:8" \
rrs="EC:2"
mc admin service restart myminio
Connection Pooling and Tuning
#!/usr/bin/env python3
"""
Optimized MinIO client configuration
"""
import boto3
from botocore.client import Config
from botocore.exceptions import ClientError
from concurrent.futures import ThreadPoolExecutor, as_completed
import time
from typing import List, Dict
class OptimizedMinIOClient:
"""Optimized MinIO client with connection pooling"""
def __init__(self, endpoint: str, access_key: str, secret_key: str,
max_pool_connections: int = 50):
"""
Initialize with optimized configuration
Args:
max_pool_connections: Maximum connections in pool (default: 50)
"""
self.config = Config(
signature_version='s3v4',
max_pool_connections=max_pool_connections,
retries={
'max_attempts': 3,
'mode': 'adaptive'
},
# TCP keepalive
tcp_keepalive=True,
# Connection timeout
connect_timeout=5,
# Read timeout
read_timeout=60,
# Enable automatic retries with exponential backoff
parameter_validation=True
)
self.client = boto3.client(
's3',
endpoint_url=endpoint,
aws_access_key_id=access_key,
aws_secret_access_key=secret_key,
config=self.config,
verify=True
)
def parallel_upload(self, bucket: str, objects: List[Dict],
max_workers: int = 10) -> List[Dict]:
"""
Upload multiple objects in parallel
Args:
bucket: Target bucket
objects: List of {'key': 'path/to/file', 'data': bytes}
max_workers: Number of parallel workers
"""
results = []
with ThreadPoolExecutor(max_workers=max_workers) as executor:
futures = {}
for obj in objects:
future = executor.submit(
self._upload_single,
bucket,
obj['key'],
obj['data']
)
futures[future] = obj['key']
for future in as_completed(futures):
key = futures[future]
try:
result = future.result()
results.append({
'key': key,
'status': 'success',
'etag': result['ETag']
})
except Exception as e:
results.append({
'key': key,
'status': 'error',
'error': str(e)
})
return results
def _upload_single(self, bucket: str, key: str, data: bytes) -> Dict:
"""Upload single object"""
return self.client.put_object(
Bucket=bucket,
Key=key,
Body=data
)
def multipart_upload(self, bucket: str, key: str, file_path: str,
part_size: int = 50 * 1024 * 1024, # 50MB
max_workers: int = 10) -> Dict:
"""
Optimized multipart upload for large files
Args:
part_size: Size of each part (default: 50MB)
max_workers: Number of parallel upload workers
"""
# Initiate multipart upload
response = self.client.create_multipart_upload(
Bucket=bucket,
Key=key
)
upload_id = response['UploadId']
try:
# Read file and split into parts
parts = []
with open(file_path, 'rb') as f:
part_number = 1
while True:
data = f.read(part_size)
if not data:
break
parts.append({
'part_number': part_number,
'data': data
})
part_number += 1
# Upload parts in parallel
uploaded_parts = []
with ThreadPoolExecutor(max_workers=max_workers) as executor:
futures = {}
for part in parts:
future = executor.submit(
self._upload_part,
bucket,
key,
upload_id,
part['part_number'],
part['data']
)
futures[future] = part['part_number']
for future in as_completed(futures):
part_number = futures[future]
try:
etag = future.result()
uploaded_parts.append({
'PartNumber': part_number,
'ETag': etag
})
except Exception as e:
# Abort on any failure
self.client.abort_multipart_upload(
Bucket=bucket,
Key=key,
UploadId=upload_id
)
raise e
# Complete multipart upload
uploaded_parts.sort(key=lambda x: x['PartNumber'])
result = self.client.complete_multipart_upload(
Bucket=bucket,
Key=key,
UploadId=upload_id,
MultipartUpload={'Parts': uploaded_parts}
)
return {
'bucket': bucket,
'key': key,
'etag': result['ETag'],
'parts': len(uploaded_parts)
}
except Exception as e:
# Abort upload on error
self.client.abort_multipart_upload(
Bucket=bucket,
Key=key,
UploadId=upload_id
)
raise e
def _upload_part(self, bucket: str, key: str, upload_id: str,
part_number: int, data: bytes) -> str:
"""Upload single part"""
response = self.client.upload_part(
Bucket=bucket,
Key=key,
UploadId=upload_id,
PartNumber=part_number,
Body=data
)
return response['ETag']
def parallel_download(self, bucket: str, keys: List[str],
max_workers: int = 10) -> List[Dict]:
"""Download multiple objects in parallel"""
results = []
with ThreadPoolExecutor(max_workers=max_workers) as executor:
futures = {}
for key in keys:
future = executor.submit(
self._download_single,
bucket,
key
)
futures[future] = key
for future in as_completed(futures):
key = futures[future]
try:
data = future.result()
results.append({
'key': key,
'status': 'success',
'size': len(data),
'data': data
})
except Exception as e:
results.append({
'key': key,
'status': 'error',
'error': str(e)
})
return results
def _download_single(self, bucket: str, key: str) -> bytes:
"""Download single object"""
response = self.client.get_object(
Bucket=bucket,
Key=key
)
return response['Body'].read()
def benchmark_throughput(self, bucket: str, object_size_mb: int = 10,
num_objects: int = 100) -> Dict:
"""Benchmark upload/download throughput"""
data = b'x' * (object_size_mb * 1024 * 1024)
# Upload benchmark
upload_start = time.time()
objects = [
{'key': f'benchmark/upload-{i}', 'data': data}
for i in range(num_objects)
]
upload_results = self.parallel_upload(bucket, objects)
upload_duration = time.time() - upload_start
# Download benchmark
keys = [obj['key'] for obj in objects]
download_start = time.time()
download_results = self.parallel_download(bucket, keys)
download_duration = time.time() - download_start
# Calculate throughput
total_mb = object_size_mb * num_objects
return {
'object_size_mb': object_size_mb,
'num_objects': num_objects,
'total_size_mb': total_mb,
'upload': {
'duration_seconds': upload_duration,
'throughput_mbps': total_mb / upload_duration,
'objects_per_second': num_objects / upload_duration
},
'download': {
'duration_seconds': download_duration,
'throughput_mbps': total_mb / download_duration,
'objects_per_second': num_objects / download_duration
}
}
# Example usage
def main():
client = OptimizedMinIOClient(
endpoint='https://minio.example.com',
access_key='your-access-key',
secret_key='your-secret-key',
max_pool_connections=50
)
# Run benchmark
print("Running throughput benchmark...")
results = client.benchmark_throughput(
bucket='benchmark',
object_size_mb=10,
num_objects=100
)
print("\nBenchmark Results:")
print(f"Upload Throughput: {results['upload']['throughput_mbps']:.2f} MB/s")
print(f"Upload Rate: {results['upload']['objects_per_second']:.2f} objects/s")
print(f"Download Throughput: {results['download']['throughput_mbps']:.2f} MB/s")
print(f"Download Rate: {results['download']['objects_per_second']:.2f} objects/s")
if __name__ == "__main__":
main()
Monitoring and Alerting
Prometheus Metrics Collection
# minio-servicemonitor.yaml
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: minio
namespace: minio-tenants
labels:
app: minio
spec:
selector:
matchLabels:
app: minio
endpoints:
- port: http-minio
interval: 30s
path: /minio/v2/metrics/cluster
scheme: https
tlsConfig:
insecureSkipVerify: true
---
# MinIO alerting rules
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: minio-alerts
namespace: minio-tenants
spec:
groups:
- name: minio
interval: 30s
rules:
# Node availability
- alert: MinIONodeDown
expr: minio_cluster_nodes_offline_total > 0
for: 5m
labels:
severity: critical
annotations:
summary: "MinIO node is down"
description: "MinIO cluster {{ $labels.server }} has {{ $value }} nodes offline"
# Disk health
- alert: MinIODiskOffline
expr: minio_cluster_disk_offline_total > 0
for: 5m
labels:
severity: critical
annotations:
summary: "MinIO disk is offline"
description: "MinIO cluster {{ $labels.server }} has {{ $value }} disks offline"
# Storage capacity
- alert: MinIOStorageSpaceLow
expr: |
(
minio_cluster_capacity_usable_free_bytes
/
minio_cluster_capacity_usable_total_bytes
) < 0.10
for: 10m
labels:
severity: warning
annotations:
summary: "MinIO storage space low"
description: "MinIO cluster {{ $labels.server }} has less than 10% free space"
- alert: MinIOStorageSpaceCritical
expr: |
(
minio_cluster_capacity_usable_free_bytes
/
minio_cluster_capacity_usable_total_bytes
) < 0.05
for: 5m
labels:
severity: critical
annotations:
summary: "MinIO storage space critical"
description: "MinIO cluster {{ $labels.server }} has less than 5% free space"
# Request rates
- alert: MinIOHighErrorRate
expr: |
rate(minio_s3_requests_errors_total[5m])
/
rate(minio_s3_requests_total[5m])
> 0.05
for: 10m
labels:
severity: warning
annotations:
summary: "MinIO high error rate"
description: "MinIO cluster {{ $labels.server }} has >5% error rate"
# Healing
- alert: MinIOHealingActive
expr: minio_heal_objects_total > 0
for: 30m
labels:
severity: info
annotations:
summary: "MinIO healing in progress"
description: "MinIO cluster {{ $labels.server }} is healing {{ $value }} objects"
# Bucket operations
- alert: MinIOSlowBucketOperations
expr: |
rate(minio_s3_requests_ttfb_seconds_sum[5m])
/
rate(minio_s3_requests_total[5m])
> 1
for: 10m
labels:
severity: warning
annotations:
summary: "MinIO slow bucket operations"
description: "MinIO cluster {{ $labels.server }} has average TTFB > 1s"
Conclusion
MinIO provides enterprise-grade object storage with S3 compatibility, making it ideal for cloud-native applications on Kubernetes. Key implementation points:
- High Availability: Deploy distributed MinIO with multiple nodes and drives
- Multi-Tenancy: Implement tenant isolation with IAM policies and quotas
- Encryption: Use KES with Vault for centralized key management
- Performance: Optimize erasure coding and connection pooling
- Monitoring: Comprehensive metrics and alerting for production operations
MinIO’s cloud-native architecture ensures scalability, reliability, and performance for object storage workloads.