Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
bendaamerahm authored Jul 5, 2023
1 parent 18b2fd2 commit 0fda1a3
Show file tree
Hide file tree
Showing 12 changed files with 270 additions and 1 deletion.
14 changes: 14 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
FROM python:3.9-alpine

# Set the working directory
WORKDIR /app

# Copy the Python files into the container
COPY main.py k8s_client.py ./

# Install additional dependencies from requirements.txt
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Set the command to run the Python script
CMD ["python", "main.py"]
71 changes: 70 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,70 @@
# leader-election
# Leader Election with Kubernetes ConfigMap

This project demonstrates a leader election algorithm using a ConfigMap in Kubernetes. The algorithm ensures that only one pod becomes the leader and performs specific tasks while other pods wait for their turn.

## Prerequisites

- Kubernetes cluster
- `kubectl` command-line tool
- Python 3.6 or later

## Setup

*1.* Clone the repository:
```bash
git clone <repository_url>
```
*2.* Navigate to the project directory:
```bash
cd leader-election-k8s-configmap
```

*3.* Install the required Python packages:

```bash
pip install -r requirements.txt
```

## Usage
Make sure you have a running Kubernetes cluster and the kubectl command-line tool properly configured to access the cluster.

Deploy the leader election application:

```bash
kubectl apply -f deployment.yaml
```
Monitor the logs of the pod to observe the leader election process:

```bash
kubectl logs -f <pod_name>
```
Replace <pod_name> with the name of the pod running the leader election application.

Modify the ConfigMap named leader-election to trigger a change in leadership:

```bash
kubectl edit configmap leader-election
```
This will open the ConfigMap in your default text editor. Modify the leader field to change the leader. Save and close the file.

Note: Ensure that the ConfigMap leader-election exists in the same namespace as the application.

Observe the logs of the leader election application to see the leader change and pod restarts.

## Cleanup
To clean up the deployed resources, run the following command:

```bash
kubectl delete -f deployment.yaml
```
This will delete the Deployment, and Kubernetes will automatically remove the associated pods.

## Troubleshooting
If the leader election process or pod restarts are not working as expected, you can check the following:

Verify that the MY_POD_NAME and MY_POD_NAMESPACE environment variables are properly set in the pod.
Check the logs of the pod for any error messages or issues.
Ensure that the ConfigMap leader-election exists in the same namespace as the application.
If you encounter any issues or have questions, please feel free to reach out for assistance.

Happy leader election in Kubernetes!
7 changes: 7 additions & 0 deletions configmap.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: leader-election
data:
leader: ""

38 changes: 38 additions & 0 deletions deployment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-deployment
labels:
app: my-app
spec:
replicas: 4
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
namespace: $(MY_POD_NAMESPACE)
spec:
serviceAccountName: pod-deleter
containers:
- name: my-container
image: leader-election:0.0.1
command: ["python", "main.py"]
env:
- name: MY_POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: MY_POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
resources:
limits:
cpu: 200m
memory: 500Mi
requests:
cpu: 100m
memory: 200Mi
67 changes: 67 additions & 0 deletions k8s_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
from kubernetes import client, config, watch
import os

class K8sClient:
def __init__(self):
# Load the kube config
config.load_incluster_config()
self.v1 = client.CoreV1Api()

def get_leader(self):
try:
# Get the 'leader-election' ConfigMap from the MY_POD_NAMESPACE namespace
pod_namespace = os.getenv('MY_POD_NAMESPACE')

config_map = self.v1.read_namespaced_config_map("leader-election", pod_namespace)
# Return the 'leader' data from the ConfigMap
return config_map.data["leader"]
except client.exceptions.ApiException as e:
# If the ConfigMap doesn't exist or 'leader' key doesn't exist, return None
if e.status == 404:
return None
else:
raise

def set_leader(self):
# Get the current pod name from the environment variable
pod_name = os.getenv('MY_POD_NAME')

# Update the 'leader' value in the 'leader-election' ConfigMap
pod_namespace = os.getenv('MY_POD_NAMESPACE')
try:
# Get the existing ConfigMap
config_map = self.v1.read_namespaced_config_map("leader-election", pod_namespace)
# Update the 'leader' key with the current pod name
config_map.data["leader"] = pod_name
# Update the ConfigMap
self.v1.replace_namespaced_config_map("leader-election", pod_namespace, config_map)
print(f"Updated leader to '{pod_name}' in the ConfigMap.")
except client.exceptions.ApiException as e:
print(f"Failed to update leader in ConfigMap: {e}")

def watch_configmap(self):
# Create a watch object
w = watch.Watch()

# Start watching the 'leader-election' ConfigMap in the 'default' namespace
pod_namespace = os.getenv('MY_POD_NAMESPACE')
for event in w.stream(self.v1.list_namespaced_config_map, pod_namespace):
# If the 'leader-election' ConfigMap is modified
if event['object'].metadata.name == "leader-election" and event['type'] == 'MODIFIED':
print("ConfigMap 'leader-election' has been modified")
# Print the new data
print(f"New data: {event['object'].data}")
# Stop watching after a change is detected
w.stop()

def restart_pod(self):
# Get the pod name from the environment variable set in the Deployment
pod_name = os.getenv("MY_POD_NAME")

# Delete the current pod
try:
pod_namespace = os.getenv('MY_POD_NAMESPACE')
self.v1.delete_namespaced_pod(pod_name, pod_namespace)
print(f"Pod {pod_name} deleted successfully. A new one will be recreated automatically.")
except client.exceptions.ApiException as e:
print(f"Failed to delete pod {pod_name}: {e}")
24 changes: 24 additions & 0 deletions main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import os
import time
from k8s_client import K8sClient



def main():
k8s = K8sClient()
pod_name = os.getenv('MY_POD_NAME')
while True:
leader = k8s.get_leader()
if leader is None or leader == pod_name:
# If there is no leader, or if this pod is the leader,
# Watch for changes in the ConfigMap
k8s.watch_configmap()
k8s.restart_pod()
time.sleep(5)
else:
# If this pod is not the leader, try to become the leader
k8s.set_leader()
time.sleep(5)

if __name__ == '__main__':
main()
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
kubernetes
9 changes: 9 additions & 0 deletions role-write.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: default
name: configmap-writer
rules:
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "update", "delete"]
9 changes: 9 additions & 0 deletions role.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: default
name: configmap-reader
rules:
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "watch", "list"]
13 changes: 13 additions & 0 deletions rolebinding-write.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: configmap-writer-binding
namespace: default
subjects:
- kind: ServiceAccount
name: default
namespace: default
roleRef:
kind: Role
name: configmap-writer
apiGroup: rbac.authorization.k8s.io
13 changes: 13 additions & 0 deletions rolebinding.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: configmap-reader-binding
namespace: default
subjects:
- kind: ServiceAccount
name: default
namespace: default
roleRef:
kind: Role
name: configmap-reader
apiGroup: rbac.authorization.k8s.io
5 changes: 5 additions & 0 deletions service-account.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: pod-deleter
namespace: default

0 comments on commit 0fda1a3

Please sign in to comment.