Skip to content

Commit e7b2aa3

Browse files
committed
initial redis operator code
1 parent 6f7b6d2 commit e7b2aa3

8 files changed

+256
-0
lines changed

Dockerfile

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Use an official lightweight Python image.
2+
FROM python:3.9-slim
3+
4+
# Set the working directory.
5+
WORKDIR /app
6+
7+
# Copy the Python requirements file and install dependencies.
8+
COPY requirements.txt /app/
9+
RUN pip install --no-cache-dir -r requirements.txt
10+
11+
# Copy the operator script.
12+
COPY redis_operator.py /app/
13+
14+
# Run the operator.
15+
CMD ["kopf", "run", "/app/redis_operator.py"]
16+

docker-compose.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
version: '3.8'
2+
3+
services:
4+
redis-operator:
5+
build: .
6+
image: schoolofdevops/redis-operator:latest
7+
environment:
8+
KUBECONFIG: /root/.kube/config
9+
volumes:
10+
- ~/.kube:/root/.kube
11+
command: kopf run /app/redis_operator.py
12+

my-redis.yaml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
apiVersion: database.example.com/v1
2+
kind: Redis
3+
metadata:
4+
name: my-redis
5+
namespace: default
6+
spec:
7+
masterSize: 1
8+
slaveSize: 2
9+
image: "redis:6.2.6" # Specify the Redis image version you prefer
10+
backupEnabled: true
11+
backupSchedule: "0 */6 * * *" # Schedule backups every 6 hours
12+
persistentVolumeSize: "200Mi" # Specify the size for the persistent volume
13+

redis-crd.yaml

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# redis-crd.yaml
2+
apiVersion: apiextensions.k8s.io/v1
3+
kind: CustomResourceDefinition
4+
metadata:
5+
name: redises.database.example.com
6+
spec:
7+
group: database.example.com
8+
versions:
9+
- name: v1
10+
served: true
11+
storage: true
12+
schema:
13+
openAPIV3Schema:
14+
type: object
15+
properties:
16+
spec:
17+
type: object
18+
properties:
19+
masterSize:
20+
type: integer
21+
default: 1
22+
slaveSize:
23+
type: integer
24+
default: 2
25+
image:
26+
type: string
27+
default: "redis:latest"
28+
backupEnabled:
29+
type: boolean
30+
default: false
31+
backupSchedule:
32+
type: string
33+
persistentVolumeSize:
34+
type: string
35+
default: "1Gi"
36+
status:
37+
type: object
38+
properties:
39+
nodes:
40+
type: array
41+
items:
42+
type: string
43+
scope: Namespaced
44+
names:
45+
plural: redises
46+
singular: redis
47+
kind: Redis
48+
shortNames:
49+
- rd

redis_operator-deploy.yaml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
apiVersion: apps/v1
2+
kind: Deployment
3+
metadata:
4+
name: redis-operator
5+
namespace: default
6+
spec:
7+
replicas: 1
8+
selector:
9+
matchLabels:
10+
app: redis-operator
11+
template:
12+
metadata:
13+
labels:
14+
app: redis-operator
15+
spec:
16+
containers:
17+
- name: redis-operator
18+
image: schoolofdevops/redis-operator:latest
19+
imagePullPolicy: Always
20+
serviceAccountName: redis-operator

redis_operator-rbac.yaml

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
apiVersion: v1
2+
kind: ServiceAccount
3+
metadata:
4+
name: redis-operator
5+
namespace: default
6+
7+
---
8+
apiVersion: rbac.authorization.k8s.io/v1
9+
kind: ClusterRole
10+
metadata:
11+
name: redis-operator-clusterrole
12+
rules:
13+
- apiGroups: [""]
14+
resources: ["pods", "pods/exec", "pods/log", "services", "endpoints", "persistentvolumeclaims", "events"]
15+
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
16+
- apiGroups: ["apps"]
17+
resources: ["deployments", "replicasets"]
18+
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
19+
- apiGroups: ["batch"]
20+
resources: ["jobs", "cronjobs"]
21+
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
22+
- apiGroups: ["database.example.com"]
23+
resources: ["redises"]
24+
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
25+
- apiGroups: ["apiextensions.k8s.io"]
26+
resources: ["customresourcedefinitions"]
27+
verbs: ["get", "list", "watch", "create", "delete"]
28+
29+
---
30+
apiVersion: rbac.authorization.k8s.io/v1
31+
kind: ClusterRoleBinding
32+
metadata:
33+
name: redis-operator-clusterrolebinding
34+
subjects:
35+
- kind: ServiceAccount
36+
name: redis-operator
37+
namespace: default
38+
roleRef:
39+
kind: ClusterRole
40+
name: redis-operator-clusterrole
41+
apiGroup: rbac.authorization.k8s.io
42+

redis_operator.py

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import kopf
2+
import kubernetes.client
3+
from kubernetes.client.rest import ApiException
4+
5+
def create_redis_deployment(name, namespace, size, role, image):
6+
# Labels to identify the pods
7+
labels = {"app": f"{name}-{role}"}
8+
return kubernetes.client.V1Deployment(
9+
api_version="apps/v1",
10+
kind="Deployment",
11+
metadata=kubernetes.client.V1ObjectMeta(name=f"{name}-{role}", namespace=namespace),
12+
spec=kubernetes.client.V1DeploymentSpec(
13+
replicas=size,
14+
selector=kubernetes.client.V1LabelSelector(
15+
match_labels=labels
16+
),
17+
template=kubernetes.client.V1PodTemplateSpec(
18+
metadata=kubernetes.client.V1ObjectMeta(
19+
labels=labels
20+
),
21+
spec=kubernetes.client.V1PodSpec(
22+
containers=[
23+
kubernetes.client.V1Container(
24+
name="redis",
25+
image=image,
26+
ports=[kubernetes.client.V1ContainerPort(container_port=6379)]
27+
)
28+
]
29+
)
30+
)
31+
)
32+
)
33+
34+
def create_backup_cron_job(name, namespace, schedule, image):
35+
# Define a CronJob resource for backups
36+
return kubernetes.client.V1CronJob(
37+
api_version="batch/v1",
38+
kind="CronJob",
39+
metadata=kubernetes.client.V1ObjectMeta(name=f"{name}-backup", namespace=namespace),
40+
spec=kubernetes.client.V1CronJobSpec(
41+
schedule=schedule,
42+
job_template=kubernetes.client.V1JobTemplateSpec(
43+
spec=kubernetes.client.V1JobSpec(
44+
template=kubernetes.client.V1PodTemplateSpec(
45+
spec=kubernetes.client.V1PodSpec(
46+
containers=[
47+
kubernetes.client.V1Container(
48+
name="backup",
49+
image=image,
50+
args=["redis-cli", "save"]
51+
)
52+
],
53+
restart_policy="OnFailure"
54+
)
55+
)
56+
)
57+
)
58+
)
59+
)
60+
61+
@kopf.on.create('database.example.com', 'v1', 'redises')
62+
def create_fn(body, name, namespace, logger, **kwargs):
63+
# Extract the spec from the body of the custom resource
64+
spec = body.get('spec', {})
65+
master_size = spec.get('masterSize', 1)
66+
slave_size = spec.get('slaveSize', 2)
67+
image = spec.get('image', 'redis:latest')
68+
backup_enabled = spec.get('backupEnabled', False)
69+
backup_schedule = spec.get('backupSchedule', '0 */12 * * *')
70+
71+
k8s_apps_v1 = kubernetes.client.AppsV1Api()
72+
73+
# Create master deployment
74+
master_deployment = create_redis_deployment(name, namespace, master_size, 'master', image)
75+
try:
76+
k8s_apps_v1.create_namespaced_deployment(namespace=namespace, body=master_deployment)
77+
logger.info(f"Master deployment for Redis '{name}' created.")
78+
except ApiException as e:
79+
logger.error(f"Failed to create master deployment: {str(e)}")
80+
raise kopf.TemporaryError(f"Failed to create master deployment: {str(e)}", delay=30)
81+
82+
# Create slave deployment
83+
slave_deployment = create_redis_deployment(name, namespace, slave_size, 'slave', image)
84+
try:
85+
k8s_apps_v1.create_namespaced_deployment(namespace=namespace, body=slave_deployment)
86+
logger.info(f"Slave deployment for Redis '{name}' created.")
87+
except ApiException as e:
88+
logger.error(f"Failed to create slave deployment: {str(e)}")
89+
raise kopf.TemporaryError(f"Failed to create slave deployment: {str(e)}", delay=30)
90+
91+
# Create a backup job if enabled
92+
if backup_enabled:
93+
backup_job = create_backup_cron_job(name, namespace, backup_schedule, image)
94+
k8s_batch_v1 = kubernetes.client.BatchV1Api()
95+
try:
96+
k8s_batch_v1.create_namespaced_cron_job(namespace=namespace, body=backup_job)
97+
logger.info(f"Backup cron job for Redis '{name}' created.")
98+
except ApiException as e:
99+
logger.error(f"Failed to create backup cron job: {str(e)}")
100+
raise kopf.TemporaryError(f"Failed to create backup cron job: {str(e)}", delay=30)

requirements.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
kopf
2+
kubernetes
3+
pyyaml
4+

0 commit comments

Comments
 (0)