diff --git a/README.md b/README.md index 984513e..4fa88a3 100644 --- a/README.md +++ b/README.md @@ -62,3 +62,7 @@ graph LR; User-- :8000 --> Traeffik ``` + +### Kubernetes with plain YML files + +There is a plain YML Kubernetes setup. Have a look at the instructions in its [README](./k8s/README.md). diff --git a/k8s/README.md b/k8s/README.md new file mode 100644 index 0000000..874bd87 --- /dev/null +++ b/k8s/README.md @@ -0,0 +1,42 @@ +# Kubernetes Deployment + +This folder contains plain YML files for a Kubernetes setup of our todo app. It is basically the +same architecture as the [Docker Compose setup](../). + +You can install this in a local Kubernetes cluster using Docker Desktop. In this case you also need +to install Traefik as Ingress controller. If you install this in some other cluster, you might +already have an ingress controller and don't need Traefik. That's up to you. + +## Prerequisites + +You need a Kubernetes cluster of some sort. If you don't have one at hand, just activate the one +in Docker Desktop. The internet tells you how. + +If you have no ingress controller in your cluster, install Traefik as follows: + +```sh +kubectl create ns traefik +kubectl -n traefik apply -f k8s/traefik +``` + +## Install ICT + +The provided YAML files assume you have a local cluster and therefore access to it via localhost. +If you're working with a "real" remote cluster, you might need to change the +[Ingress Resource](./apps/ingress.yaml). + +```sh +kubectl create namespace todo +kubectl --namespace todo apply -f k8s/services +kubectl --namespace todo apply -f k8s/apps +``` + +In a remote cluster, you should be done and be able to open the app in your browser. + +In a local cluster, we now need to open a port-forward to Traefik. Then we can access the app on +localhost as usual. + +```sh +kubectl --namespace traefik port-forward services/traefik-web 8000:80 +open localhost:8000 +``` diff --git a/k8s/apps/frontend.yaml b/k8s/apps/frontend.yaml new file mode 100644 index 0000000..3a687f4 --- /dev/null +++ b/k8s/apps/frontend.yaml @@ -0,0 +1,53 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: frontend +spec: + replicas: 2 + selector: + matchLabels: + app: frontend + template: + metadata: + labels: + app: frontend + spec: + containers: + - name: frontend + image: ghcr.io/bespinian/insanely-complex-todo/frontend:latest + readinessProbe: + initialDelaySeconds: 1 + periodSeconds: 10 + httpGet: + port: 80 + path: / + livenessProbe: + initialDelaySeconds: 1 + periodSeconds: 10 + httpGet: + port: 80 + path: / + resources: + requests: + memory: "50Mi" + cpu: "50m" + limits: + memory: "128Mi" + cpu: "500m" + ports: + - containerPort: 80 + name: web +--- +apiVersion: v1 +kind: Service +metadata: + name: frontend +spec: + ports: + - port: 80 + protocol: TCP + targetPort: web + name: web + selector: + app: frontend + type: ClusterIP diff --git a/k8s/apps/ingress.yaml b/k8s/apps/ingress.yaml new file mode 100644 index 0000000..3667b6c --- /dev/null +++ b/k8s/apps/ingress.yaml @@ -0,0 +1,29 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: frontend +spec: + rules: + - http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: frontend + port: + number: 80 + - path: /api/tasks + pathType: Prefix + backend: + service: + name: tasks + port: + number: 80 + - path: /ws + pathType: Prefix + backend: + service: + name: websocket-server + port: + number: 80 diff --git a/k8s/apps/tasks-api.yaml b/k8s/apps/tasks-api.yaml new file mode 100644 index 0000000..d383c4b --- /dev/null +++ b/k8s/apps/tasks-api.yaml @@ -0,0 +1,54 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: tasks +spec: + replicas: 2 + selector: + matchLabels: + app: tasks + template: + metadata: + labels: + app: tasks + spec: + containers: + - name: tasks + image: ghcr.io/bespinian/insanely-complex-todo/tasks:latest + env: + - name: MONGODB_URI + value: mongodb://mongo + - name: REDIS_URL + value: redis://redis/0 + readinessProbe: + initialDelaySeconds: 1 + periodSeconds: 10 + httpGet: + port: 3000 + path: livez + livenessProbe: + initialDelaySeconds: 1 + periodSeconds: 10 + httpGet: + port: 3000 + path: readyz + resources: + requests: + memory: "50Mi" + cpu: "50m" + limits: + memory: "128Mi" + cpu: "500m" +--- +apiVersion: v1 +kind: Service +metadata: + name: tasks +spec: + ports: + - port: 80 + protocol: TCP + targetPort: 3000 + selector: + app: tasks + type: ClusterIP diff --git a/k8s/apps/websocket-server.yaml b/k8s/apps/websocket-server.yaml new file mode 100644 index 0000000..7efe8d5 --- /dev/null +++ b/k8s/apps/websocket-server.yaml @@ -0,0 +1,44 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: websocket-server +spec: + replicas: 2 + selector: + matchLabels: + app: websocket-server + template: + metadata: + labels: + app: websocket-server + spec: + containers: + - name: websocket-server + image: ghcr.io/bespinian/insanely-complex-todo/websocket-server:latest + env: + - name: REDIS_URL + value: redis://redis/0 + - name: WEBSOCKET_SERVER_HOST + value: "0.0.0.0" + - name: WEBSOCKET_SERVER_PORT + value: "8765" + resources: + requests: + memory: "128Mi" + cpu: "100m" + limits: + memory: "512Mi" + cpu: "500m" +--- +apiVersion: v1 +kind: Service +metadata: + name: websocket-server +spec: + ports: + - port: 80 + protocol: TCP + targetPort: 8765 + selector: + app: websocket-server + type: ClusterIP diff --git a/k8s/services/mongodb.yaml b/k8s/services/mongodb.yaml new file mode 100644 index 0000000..5569077 --- /dev/null +++ b/k8s/services/mongodb.yaml @@ -0,0 +1,58 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: mongo +spec: + replicas: 1 + selector: + matchLabels: + app: mongo + template: + metadata: + labels: + app: mongo + spec: + containers: + - name: mongo + image: mongo:8.0 + resources: + requests: + memory: "100Mi" + cpu: "100m" + limits: + memory: "256Mi" + cpu: "500m" + ports: + - containerPort: 27017 + volumeMounts: + - name: mongo-data + mountPath: /data/db + volumes: + - name: mongo-data + persistentVolumeClaim: + claimName: mongo-data +--- +apiVersion: v1 +kind: Service +metadata: + name: mongo +spec: + ports: + - port: 27017 + protocol: TCP + targetPort: 27017 + selector: + app: mongo + type: ClusterIP +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: mongo-data +spec: + resources: + requests: + storage: 50M + volumeMode: Filesystem + accessModes: + - ReadWriteOnce diff --git a/k8s/services/redis.yaml b/k8s/services/redis.yaml new file mode 100644 index 0000000..b7ac23c --- /dev/null +++ b/k8s/services/redis.yaml @@ -0,0 +1,42 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: redis +spec: + replicas: 1 + selector: + matchLabels: + app: redis + template: + metadata: + labels: + app: redis + spec: + containers: + - name: redis + image: redis:7-alpine + resources: + requests: + memory: "50Mi" + cpu: "100m" + limits: + memory: "256Mi" + cpu: "500m" + readinessProbe: + exec: + command: ["redis-cli", "--raw", "incr", "ping"] + ports: + - containerPort: 6379 +--- +apiVersion: v1 +kind: Service +metadata: + name: redis +spec: + ports: + - port: 6379 + protocol: TCP + targetPort: 6379 + selector: + app: redis + type: ClusterIP diff --git a/k8s/traefik/deployment.yaml b/k8s/traefik/deployment.yaml new file mode 100644 index 0000000..1725e3e --- /dev/null +++ b/k8s/traefik/deployment.yaml @@ -0,0 +1,37 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: traefik + namespace: traefik +spec: + replicas: 1 + selector: + matchLabels: + app: traefik + template: + metadata: + labels: + app: traefik + spec: + serviceAccountName: traefik-account + containers: + - name: traefik + image: traefik:v3.1 + args: + - "--log.level=info" + - "--api.insecure=true" + - "--entryPoints.web.address=:80" + - "--providers.kubernetesingress" + - "--ping.entrypoint=traefik" + resources: + requests: + memory: "50Mi" + cpu: "100m" + limits: + memory: "128Mi" + cpu: "500m" + ports: + - containerPort: 80 + name: web + - containerPort: 8080 + name: dashboard diff --git a/k8s/traefik/rbac.yaml b/k8s/traefik/rbac.yaml new file mode 100644 index 0000000..73fdbeb --- /dev/null +++ b/k8s/traefik/rbac.yaml @@ -0,0 +1,75 @@ +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: traefik-role +rules: + - apiGroups: + - "" + resources: + - services + - secrets + - nodes + verbs: + - get + - list + - watch + - apiGroups: + - discovery.k8s.io + resources: + - endpointslices + verbs: + - list + - watch + - apiGroups: + - extensions + - networking.k8s.io + resources: + - ingresses + - ingressclasses + verbs: + - get + - list + - watch + - apiGroups: + - extensions + - networking.k8s.io + resources: + - ingresses/status + verbs: + - update + - apiGroups: + - traefik.io + resources: + - middlewares + - middlewaretcps + - ingressroutes + - traefikservices + - ingressroutetcps + - ingressrouteudps + - tlsoptions + - tlsstores + - serverstransports + - serverstransporttcps + verbs: + - get + - list + - watch +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: traefik-account + namespace: traefik +--- +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: traefik-role-binding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: traefik-role +subjects: + - kind: ServiceAccount + name: traefik-account + namespace: traefik diff --git a/k8s/traefik/service.yaml b/k8s/traefik/service.yaml new file mode 100644 index 0000000..13a88c0 --- /dev/null +++ b/k8s/traefik/service.yaml @@ -0,0 +1,27 @@ +apiVersion: v1 +kind: Service +metadata: + name: traefik-web + namespace: traefik +spec: + ports: + - port: 80 + protocol: TCP + targetPort: web + selector: + app: traefik + type: ClusterIP +--- +apiVersion: v1 +kind: Service +metadata: + name: traefik-dashboard + namespace: traefik +spec: + ports: + - port: 8080 + protocol: TCP + targetPort: dashboard + selector: + app: traefik + type: ClusterIP