Skip to content

Commit 9b2c8dd

Browse files
committed
✨ Add Kubernetes deployment, service, and ingress configurations for KidsMedia application
1 parent 765b8cb commit 9b2c8dd

4 files changed

Lines changed: 289 additions & 0 deletions

File tree

k8s/README.md

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
# Kubernetes Deployment Configuration
2+
3+
This directory contains Kubernetes configuration files for deploying the KidsMedia Phoenix LiveView application.
4+
5+
## Files
6+
7+
- `deployment.yaml` - Main application deployment with secrets
8+
- `service.yaml` - ClusterIP service to expose the application internally
9+
- `ingress.yaml` - Ingress configuration with NGINX and SSL/TLS
10+
11+
## Prerequisites
12+
13+
1. **Kubernetes cluster** with NGINX Ingress Controller installed
14+
2. **cert-manager** (optional, for automatic SSL certificates)
15+
3. **Container registry access** to GitHub Container Registry (GHCR)
16+
17+
## Configuration Steps
18+
19+
### 1. Update Secrets
20+
21+
Edit `deployment.yaml` and replace the placeholder values:
22+
23+
```yaml
24+
stringData:
25+
secret-key-base: "CHANGE_ME_64_CHAR_SECRET_KEY_BASE_FOR_PHOENIX_APP_SECURITY"
26+
unsplash-access-key: "YOUR_UNSPLASH_ACCESS_KEY_HERE"
27+
```
28+
29+
Generate a secret key base:
30+
```bash
31+
mix phx.gen.secret
32+
```
33+
34+
### 2. Configure Container Registry Access
35+
36+
If using a private registry, create the Docker config secret:
37+
38+
```bash
39+
kubectl create secret docker-registry ghcr-secret \
40+
--docker-server=ghcr.io \
41+
--docker-username=YOUR_GITHUB_USERNAME \
42+
--docker-password=YOUR_GITHUB_TOKEN \
43+
--docker-email=YOUR_EMAIL
44+
```
45+
46+
For public images, remove the `imagePullSecrets` section from `deployment.yaml`.
47+
48+
### 3. Update Domain
49+
50+
Replace `kids-media.example.com` in both `deployment.yaml` (PHX_HOST) and `ingress.yaml` with your actual domain.
51+
52+
### 4. SSL/TLS Configuration
53+
54+
The ingress is configured for automatic SSL using cert-manager. If you don't have cert-manager:
55+
56+
- Remove the `cert-manager.io/cluster-issuer` annotation
57+
- Remove the `tls` section from ingress
58+
- Set `nginx.ingress.kubernetes.io/ssl-redirect: "false"`
59+
60+
## Deployment
61+
62+
Apply the configurations in order:
63+
64+
```bash
65+
# Apply deployment and service
66+
kubectl apply -f k8s/deployment.yaml
67+
kubectl apply -f k8s/service.yaml
68+
69+
# Apply ingress
70+
kubectl apply -f k8s/ingress.yaml
71+
```
72+
73+
Or apply all at once:
74+
```bash
75+
kubectl apply -f k8s/
76+
```
77+
78+
## Verification
79+
80+
Check deployment status:
81+
```bash
82+
# Check pods
83+
kubectl get pods -l app=kids-media
84+
85+
# Check service
86+
kubectl get service kids-media-service
87+
88+
# Check ingress
89+
kubectl get ingress kids-media-ingress
90+
91+
# Check logs
92+
kubectl logs -l app=kids-media
93+
```
94+
95+
## Scaling
96+
97+
Scale the application:
98+
```bash
99+
kubectl scale deployment kids-media --replicas=5
100+
```
101+
102+
## Environment Variables
103+
104+
The application uses these environment variables:
105+
106+
- `PORT`: Application port (4000)
107+
- `SECRET_KEY_BASE`: Phoenix secret key base (from secret)
108+
- `UNSPLASH_ACCESS_KEY`: Unsplash API key (from secret)
109+
- `PHX_HOST`: External hostname for Phoenix
110+
- `MIX_ENV`: Elixir environment (prod)
111+
112+
## Resource Limits
113+
114+
Current resource configuration:
115+
- **Requests**: 256Mi memory, 250m CPU
116+
- **Limits**: 512Mi memory, 500m CPU
117+
118+
Adjust based on your cluster capacity and application needs.
119+
120+
## Health Checks
121+
122+
The deployment includes:
123+
- **Liveness probe**: Checks if app is running (restarts if failing)
124+
- **Readiness probe**: Checks if app can receive traffic
125+
- **Startup probe**: Gives extra time during initial startup
126+
127+
## Troubleshooting
128+
129+
Common issues:
130+
131+
1. **ImagePullBackOff**: Check registry credentials and image name
132+
2. **CrashLoopBackOff**: Check logs for application errors
133+
3. **503 errors**: Check service selector and pod labels match
134+
4. **SSL issues**: Verify cert-manager is working and domain is correct
135+
136+
Check application logs:
137+
```bash
138+
kubectl logs -l app=kids-media --tail=100
139+
```
140+
141+
## Production Considerations
142+
143+
- Use a proper secret management solution (e.g., HashiCorp Vault, AWS Secrets Manager)
144+
- Set up monitoring and alerting
145+
- Configure resource limits based on load testing
146+
- Use horizontal pod autoscaling (HPA) for automatic scaling
147+
- Consider using a service mesh for advanced networking features

k8s/deployment.yaml

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
apiVersion: apps/v1
2+
kind: Deployment
3+
metadata:
4+
name: kids-media
5+
labels:
6+
app: kids-media
7+
spec:
8+
replicas: 3
9+
selector:
10+
matchLabels:
11+
app: kids-media
12+
template:
13+
metadata:
14+
labels:
15+
app: kids-media
16+
spec:
17+
containers:
18+
- name: kids-media
19+
image: ghcr.io/royveshovda/kids_media:latest
20+
ports:
21+
- containerPort: 4000
22+
protocol: TCP
23+
env:
24+
- name: PORT
25+
value: "4000"
26+
- name: SECRET_KEY_BASE
27+
valueFrom:
28+
secretKeyRef:
29+
name: kids-media-secrets
30+
key: secret-key-base
31+
- name: UNSPLASH_ACCESS_KEY
32+
valueFrom:
33+
secretKeyRef:
34+
name: kids-media-secrets
35+
key: unsplash-access-key
36+
- name: PHX_HOST
37+
value: "kids-media.example.com"
38+
- name: MIX_ENV
39+
value: "prod"
40+
resources:
41+
requests:
42+
memory: "256Mi"
43+
cpu: "250m"
44+
limits:
45+
memory: "512Mi"
46+
cpu: "500m"
47+
livenessProbe:
48+
httpGet:
49+
path: /
50+
port: 4000
51+
initialDelaySeconds: 30
52+
periodSeconds: 10
53+
timeoutSeconds: 5
54+
failureThreshold: 3
55+
readinessProbe:
56+
httpGet:
57+
path: /
58+
port: 4000
59+
initialDelaySeconds: 5
60+
periodSeconds: 5
61+
timeoutSeconds: 3
62+
failureThreshold: 2
63+
startupProbe:
64+
httpGet:
65+
path: /
66+
port: 4000
67+
initialDelaySeconds: 10
68+
periodSeconds: 10
69+
timeoutSeconds: 5
70+
failureThreshold: 6
71+
imagePullSecrets:
72+
- name: ghcr-secret
73+
---
74+
apiVersion: v1
75+
kind: Secret
76+
metadata:
77+
name: kids-media-secrets
78+
type: Opaque
79+
stringData:
80+
secret-key-base: "CHANGE_ME_64_CHAR_SECRET_KEY_BASE_FOR_PHOENIX_APP_SECURITY"
81+
unsplash-access-key: "YOUR_UNSPLASH_ACCESS_KEY_HERE"
82+
---
83+
apiVersion: v1
84+
kind: Secret
85+
metadata:
86+
name: ghcr-secret
87+
type: kubernetes.io/dockerconfigjson
88+
data:
89+
.dockerconfigjson: BASE64_ENCODED_DOCKER_CONFIG_JSON

k8s/ingress.yaml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
apiVersion: networking.k8s.io/v1
2+
kind: Ingress
3+
metadata:
4+
name: kids-media-ingress
5+
labels:
6+
app: kids-media
7+
annotations:
8+
# NGINX Ingress Controller annotations
9+
nginx.ingress.kubernetes.io/rewrite-target: /
10+
nginx.ingress.kubernetes.io/ssl-redirect: "true"
11+
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
12+
13+
# Let's Encrypt certificate manager (if using cert-manager)
14+
cert-manager.io/cluster-issuer: "letsencrypt-prod"
15+
16+
# Optional: Increase proxy body size for large uploads
17+
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
18+
19+
# Optional: Enable CORS if needed
20+
# nginx.ingress.kubernetes.io/enable-cors: "true"
21+
# nginx.ingress.kubernetes.io/cors-allow-origin: "*"
22+
spec:
23+
ingressClassName: nginx
24+
tls:
25+
- hosts:
26+
- kids-media.example.com
27+
secretName: kids-media-tls
28+
rules:
29+
- host: kids-media.example.com
30+
http:
31+
paths:
32+
- path: /
33+
pathType: Prefix
34+
backend:
35+
service:
36+
name: kids-media-service
37+
port:
38+
number: 80

k8s/service.yaml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
apiVersion: v1
2+
kind: Service
3+
metadata:
4+
name: kids-media-service
5+
labels:
6+
app: kids-media
7+
spec:
8+
type: ClusterIP
9+
ports:
10+
- port: 80
11+
targetPort: 4000
12+
protocol: TCP
13+
name: http
14+
selector:
15+
app: kids-media

0 commit comments

Comments
 (0)