Skip to content

Commit 0899a2f

Browse files
initial commit
0 parents  commit 0899a2f

8 files changed

Lines changed: 322 additions & 0 deletions

File tree

.github/workflows/build.yaml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
name: Build and Deploy Vue.js App
2+
3+
on:
4+
workflow_dispatch:
5+
push:
6+
branches:
7+
- main # Set to the branch name you use for releases
8+
tags: ['v*.*.*']
9+
pull_request:
10+
branches:
11+
- main
12+
13+
jobs:
14+
call-docker-build:
15+
uses: ethdevops/workflows/.github/workflows/basic-docker-build.yaml@main
16+
secrets:
17+
docker_registry_user: ${{ secrets.DOCKER_REGISTRY_USER }}
18+
docker_registry_password: ${{ secrets.DOCKER_REGISTRY_SECRET }}
19+

Dockerfile

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
FROM python:3.11-slim
2+
3+
WORKDIR /app
4+
5+
COPY requirements.txt .
6+
RUN pip install --no-cache-dir -r requirements.txt
7+
8+
COPY service_monitor.py .
9+
RUN chmod +x service_monitor.py
10+
11+
CMD ["./service_monitor.py"]

README.md

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
# Kubernetes LoadBalancer Service Monitor
2+
3+
This tool monitors Kubernetes LoadBalancer services for changes and triggers GitHub Actions workflows in response to those changes.
4+
5+
## Overview
6+
7+
The service monitor watches for any changes (creation, modification, or deletion) to LoadBalancer services across all namespaces in your Kubernetes cluster. When a change is detected, it triggers the `ansible.yaml` workflow in the `ethdevops/internal-stack-iac` repository with specific parameters.
8+
9+
## Prerequisites
10+
11+
- Kubernetes cluster access
12+
- GitHub Personal Access Token with workflow permissions
13+
- Docker (for building the container image)
14+
- kubectl configured with cluster access
15+
16+
## Configuration
17+
18+
### 1. Build the Docker Image
19+
20+
```bash
21+
docker build -t your-registry/service-monitor:latest .
22+
docker push your-registry/service-monitor:latest
23+
```
24+
25+
### 2. Create GitHub Token Secret
26+
27+
Create a GitHub Personal Access Token with workflow permissions and create the secret:
28+
29+
```bash
30+
# Replace YOUR_GITHUB_TOKEN with your actual token
31+
kubectl create namespace monitoring
32+
echo -n 'YOUR_GITHUB_TOKEN' | base64 | kubectl create secret generic github-token \
33+
--namespace monitoring \
34+
--from-file=GITHUB_TOKEN=/dev/stdin
35+
```
36+
37+
### 3. Update Deployment Image
38+
39+
Edit `k8s/deployment.yaml` and update the image field with your registry path:
40+
41+
```yaml
42+
image: your-registry/service-monitor:latest
43+
```
44+
45+
### 4. Deploy to Kubernetes
46+
47+
```bash
48+
kubectl apply -f k8s/rbac.yaml
49+
kubectl apply -f k8s/deployment.yaml
50+
```
51+
52+
## Verification
53+
54+
Check if the pod is running:
55+
56+
```bash
57+
kubectl get pods -n monitoring
58+
```
59+
60+
View the logs:
61+
62+
```bash
63+
kubectl logs -n monitoring -l app=service-monitor -f
64+
```
65+
66+
## How It Works
67+
68+
1. The service monitor uses the Kubernetes API to watch for changes in LoadBalancer services
69+
2. When a change is detected, it triggers the GitHub Actions workflow with:
70+
- Tenant: ethquokkaops
71+
- Project: colo-loadbalancers
72+
73+
## Troubleshooting
74+
75+
### Check Pod Status
76+
```bash
77+
kubectl describe pod -n monitoring -l app=service-monitor
78+
```
79+
80+
### Check Logs
81+
```bash
82+
kubectl logs -n monitoring -l app=service-monitor -f
83+
```
84+
85+
### Common Issues
86+
87+
1. **Pod can't pull image**: Check your image registry credentials and image path
88+
2. **Permission denied**: Verify RBAC permissions are correctly configured
89+
3. **GitHub workflow not triggering**: Check the GitHub token permissions and validity
90+
91+
## Security Considerations
92+
93+
- The service runs with minimal permissions using RBAC
94+
- The container runs as a non-root user
95+
- The filesystem is read-only
96+
- The container has resource limits defined
97+
98+
## Maintenance
99+
100+
- Regularly update the dependencies in `requirements.txt`
101+
- Monitor the pod's resource usage and adjust limits as needed
102+
- Rotate the GitHub token periodically
103+
- Keep the Docker base image updated for security patches

k8s/deployment.yaml

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
apiVersion: apps/v1
2+
kind: Deployment
3+
metadata:
4+
name: service-monitor
5+
namespace: monitoring
6+
labels:
7+
app: service-monitor
8+
spec:
9+
replicas: 1
10+
selector:
11+
matchLabels:
12+
app: service-monitor
13+
template:
14+
metadata:
15+
labels:
16+
app: service-monitor
17+
spec:
18+
serviceAccountName: service-monitor
19+
containers:
20+
- name: service-monitor
21+
image: service-monitor:latest # Update this with your registry/image:tag
22+
imagePullPolicy: Always
23+
env:
24+
- name: GITHUB_TOKEN
25+
valueFrom:
26+
secretKeyRef:
27+
name: github-token
28+
key: GITHUB_TOKEN
29+
resources:
30+
requests:
31+
cpu: "100m"
32+
memory: "128Mi"
33+
limits:
34+
cpu: "200m"
35+
memory: "256Mi"
36+
securityContext:
37+
allowPrivilegeEscalation: false
38+
runAsNonRoot: true
39+
runAsUser: 1000
40+
readOnlyRootFilesystem: true

k8s/rbac.yaml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
apiVersion: v1
2+
kind: ServiceAccount
3+
metadata:
4+
name: service-monitor
5+
namespace: monitoring
6+
---
7+
apiVersion: rbac.authorization.k8s.io/v1
8+
kind: ClusterRole
9+
metadata:
10+
name: service-monitor
11+
rules:
12+
- apiGroups: [""]
13+
resources: ["services"]
14+
verbs: ["get", "watch", "list"]
15+
---
16+
apiVersion: rbac.authorization.k8s.io/v1
17+
kind: ClusterRoleBinding
18+
metadata:
19+
name: service-monitor
20+
subjects:
21+
- kind: ServiceAccount
22+
name: service-monitor
23+
namespace: monitoring
24+
roleRef:
25+
kind: ClusterRole
26+
name: service-monitor
27+
apiGroup: rbac.authorization.k8s.io

k8s/secret.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
apiVersion: v1
2+
kind: Secret
3+
metadata:
4+
name: github-token
5+
namespace: monitoring
6+
type: Opaque
7+
data:
8+
# Replace this with your base64 encoded GitHub token
9+
# Example: echo -n "your-token" | base64
10+
GITHUB_TOKEN: "YOUR_BASE64_ENCODED_TOKEN"

requirements.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
kubernetes==29.0.0
2+
PyGithub==2.2.0

service_monitor.py

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
#!/usr/bin/env python3
2+
3+
import os
4+
import logging
5+
from kubernetes import client, config, watch
6+
from github import Github
7+
8+
# Configure logging
9+
logging.basicConfig(
10+
level=logging.INFO,
11+
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
12+
)
13+
logger = logging.getLogger(__name__)
14+
15+
# GitHub configuration
16+
GITHUB_TOKEN = os.environ.get('GITHUB_TOKEN')
17+
GITHUB_REPO = "ethdevops/internal-stack-iac"
18+
WORKFLOW_FILE = "ansible.yaml"
19+
TENANT = "ethquokkaops"
20+
PROJECT = "colo-loadbalancers"
21+
22+
def trigger_github_workflow(gh_token, event_type):
23+
"""
24+
Trigger GitHub Actions workflow with the specified parameters
25+
"""
26+
try:
27+
g = Github(gh_token)
28+
repo = g.get_repo(GITHUB_REPO)
29+
30+
# Get the workflow by filename
31+
workflow = None
32+
for wf in repo.get_workflows():
33+
if wf.path.endswith(WORKFLOW_FILE):
34+
workflow = wf
35+
break
36+
37+
if workflow is None:
38+
logger.error(f"Workflow {WORKFLOW_FILE} not found")
39+
return False
40+
41+
# Create workflow dispatch event with inputs
42+
workflow.create_dispatch(
43+
ref="main", # You might want to make this configurable
44+
inputs={
45+
"tenant": TENANT,
46+
"project": PROJECT
47+
}
48+
)
49+
50+
logger.info(f"Successfully triggered workflow for event: {event_type}")
51+
return True
52+
53+
except Exception as e:
54+
logger.error(f"Failed to trigger workflow: {str(e)}")
55+
return False
56+
57+
def watch_services():
58+
"""
59+
Watch for changes in LoadBalancer services
60+
"""
61+
try:
62+
# Try to load in-cluster config first
63+
try:
64+
config.load_incluster_config()
65+
except config.ConfigException:
66+
config.load_kube_config()
67+
68+
v1 = client.CoreV1Api()
69+
w = watch.Watch()
70+
71+
logger.info("Starting to watch LoadBalancer services...")
72+
73+
for event in w.stream(v1.list_service_for_all_namespaces):
74+
service = event['object']
75+
76+
# Only process LoadBalancer services
77+
if service.spec.type == 'LoadBalancer':
78+
event_type = event['type']
79+
logger.info(f"LoadBalancer service event: {event_type} - {service.metadata.namespace}/{service.metadata.name}")
80+
81+
# Trigger workflow for relevant events
82+
if event_type in ['ADDED', 'MODIFIED', 'DELETED']:
83+
if GITHUB_TOKEN:
84+
trigger_github_workflow(GITHUB_TOKEN, event_type)
85+
else:
86+
logger.error("GITHUB_TOKEN environment variable not set")
87+
88+
except Exception as e:
89+
logger.error(f"Error watching services: {str(e)}")
90+
raise
91+
92+
def main():
93+
"""
94+
Main function to start the service monitor
95+
"""
96+
if not GITHUB_TOKEN:
97+
logger.error("GITHUB_TOKEN environment variable must be set")
98+
exit(1)
99+
100+
while True:
101+
try:
102+
watch_services()
103+
except Exception as e:
104+
logger.error(f"Error in main loop: {str(e)}")
105+
# Wait a bit before retrying
106+
import time
107+
time.sleep(5)
108+
109+
if __name__ == "__main__":
110+
main()

0 commit comments

Comments
 (0)